JIT: optimizations for multi-use boxes #9118

Description

@AndyAyersMS

A fairly common pattern (especially after inlining) is to see a box that feeds an isinst and if that succeeds, an unbox.any. For example:

usingSystem;usingSystem.Collections.Generic;usingSystem.Runtime.CompilerServices;internalclassObjectEqualityComparer<T>:EqualityComparer<T>{[MethodImpl(MethodImplOptions.AggressiveInlining)]publicoverrideboolEquals(Tx,Ty){if(x!=null){if(y!=null)returnx.Equals(y);returnfalse;}if(y!=null)returnfalse;returntrue;}[MethodImpl(MethodImplOptions.AggressiveInlining)]publicoverrideintGetHashCode(Tobj)=>obj?.GetHashCode()??0;// Equals method for the comparer itself.publicoverrideboolEquals(Objectobj)=>obj!=null&&GetType()==obj.GetType();publicoverrideintGetHashCode()=>GetType().GetHashCode();}classC{publicstaticintMain(){varcomp=newObjectEqualityComparer<int>();boolresult=comp.Equals(3,4);returnresult?0:100;}}

We get pretty far when optimizing Main here -- we can devirtualize the call to Equals, inline it and remove the null checks since we have a value type, then inline the inner call to Equals. But along the way we have to box y and the inner Equals has the following IL:

IL_0000 03ldarg.1 IL_0001 75 f1 000002isinst0x20000F1
IL_0006 2d 02brtrue.s2 (IL_000a)
IL_0008 16ldc.i4.0 IL_0009 2a ret IL_000a 02ldarg.0 IL_000b 4a ldind.i4 IL_000c 03ldarg.1 IL_000d a5 f1 000002unbox.any0x20000F1
IL_0012 fe 01ceq IL_0014 2a ret

With the advent of dotnet/coreclr#14420 the jit will now optimize away the isinst, but the box cleanup opts for unbox.any don't fire because there is usually a temp in the way, and so we generate the following code for Main:

G_M4930_IG01:57pushrdi56pushrsi 4883EC28 subrsp,40G_M4930_IG02:; ** BOX (y) ** 48B9086014E2FA7F0000 movrcx,0x7FFAE2146008 E8BB3C835F call CORINFO_HELP_NEWSFAST  C7400804000000 mov dword ptr [rax+8],4 488BF0 movrsi,rax 4885F6 testrsi,rsi ; gratuitous null check ?7504jne SHORT G_M4930_IG03 33FF xoredi,edi EB2D jmp SHORT G_M4930_IG05G_M4930_IG03:; * UNBOX.ANY type check 48BA086014E2FA7F0000 movrdx,0x7FFAE2146008483916cmp qword ptr [rsi],rdx7412je SHORT G_M4930_IG04; * call helper if type check fails (which it won't) 488BD6 movrdx,rsi 48B9086014E2FA7F0000 movrcx,0x7FFAE2146008 E8470B395F call CORINFO_HELP_UNBOXG_M4930_IG04: 837E0803 cmp dword ptr [rsi+8],3 400F94C7 sete dil 400FB6FF movzxrdi, dilG_M4930_IG05: 85FF testedi,edi 750C jne SHORT G_M4930_IG07 B864000000 moveax,100G_M4930_IG06: 4883C428 addrsp,40 5E poprsi 5F poprdi C3 retG_M4930_IG07: 33C0 xoreax,eaxG_M4930_IG08: 4883C428 addrsp,40 5E poprsi 5F poprdi C3 ret

If when optimizing a successful cast we copy the result to a new more strongly typed temp (see #9117) we might be able to optimize away the type equality check in the downstream unbox.any (see dotnet/coreclr#14473). And perhaps if we are lucky and the box is simple we might be able to propagate the value to be boxed through the box/unbox to the ultimate use, and so not need the unbox. But the box would remain as it is difficult to remove unless it is known to be dead and whatever transformation makes it dead explicitly cleans it up.

A couple of ways we could approach this:

  • The optimizer should be able to reason about and propagate boxes and perhaps trigger the box/unbox.any peephole, and turn the result into a simple copy.
  • BOX is just an expression "wrapper" in the spirit of JIT: some ideas on high-level representation of runtime operations in IR #9056. So we could allow the inliner to give BOX(y) the same treatment as y and duplicate it within the inlinee body (essentially, generalize the logic in impInlineFetchArg that begins with else if (argInfo.argIsLclVar && !argCanBeModified) to also apply to BOX(y)). If we added suitable "reference counting" to boxes to track the duplicates then optimizing away the last use of the box could trigger the box cleanup. We have this today but the reference count is implicit and always = 1 since we don't duplicate the boxed values.

If all this kicked in, the code for Main above would collapse to simply returning a constant.

category:cq
theme:importer
skill-level:expert
cost:medium

Metadata

Metadata

Labels

Priority:2Work that is important, but not critical for the releasearea-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMIenhancementProduct code improvement that does NOT require public API changes/additionsoptimizationtenet-performancePerformance related issue

Type

No type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

    , 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
     blocks
    (function() {
    function addCopyButtons() {
    document.querySelectorAll('pre code').forEach(function(codeBlock) {
    if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
    codeBlock.parentElement.setAttribute('data-copy-added', 'true');
    var btn = document.createElement('button');
    btn.textContent = 'Copy';
    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;';
    btn.onmouseover = function() { this.style.opacity = '1'; };
    btn.onmouseout = function() { this.style.opacity = '0.7'; };
    btn.onclick = function() {
    navigator.clipboard.writeText(codeBlock.textContent).then(function() {
    btn.textContent = 'Copied!';
    setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
    });
    };
    codeBlock.parentElement.style.position = 'relative';
    codeBlock.parentElement.appendChild(btn);
    });
    }
    addCopyButtons();
    // Re-run on dynamic content
    var observer = new MutationObserver(addCopyButtons);
    observer.observe(document.body, { childList: true, subtree: true });
    })();
    }
    } 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

    JIT: optimizations for multi-use boxes #9118

    Description

    @AndyAyersMS

    A fairly common pattern (especially after inlining) is to see a box that feeds an isinst and if that succeeds, an unbox.any. For example:

    usingSystem;usingSystem.Collections.Generic;usingSystem.Runtime.CompilerServices;internalclassObjectEqualityComparer<T>:EqualityComparer<T>{[MethodImpl(MethodImplOptions.AggressiveInlining)]publicoverrideboolEquals(Tx,Ty){if(x!=null){if(y!=null)returnx.Equals(y);returnfalse;}if(y!=null)returnfalse;returntrue;}[MethodImpl(MethodImplOptions.AggressiveInlining)]publicoverrideintGetHashCode(Tobj)=>obj?.GetHashCode()??0;// Equals method for the comparer itself.publicoverrideboolEquals(Objectobj)=>obj!=null&&GetType()==obj.GetType();publicoverrideintGetHashCode()=>GetType().GetHashCode();}classC{publicstaticintMain(){varcomp=newObjectEqualityComparer<int>();boolresult=comp.Equals(3,4);returnresult?0:100;}}

    We get pretty far when optimizing Main here -- we can devirtualize the call to Equals, inline it and remove the null checks since we have a value type, then inline the inner call to Equals. But along the way we have to box y and the inner Equals has the following IL:

    IL_0000 03ldarg.1 IL_0001 75 f1 000002isinst0x20000F1
    IL_0006 2d 02brtrue.s2 (IL_000a)
    IL_0008 16ldc.i4.0 IL_0009 2a ret IL_000a 02ldarg.0 IL_000b 4a ldind.i4 IL_000c 03ldarg.1 IL_000d a5 f1 000002unbox.any0x20000F1
    IL_0012 fe 01ceq IL_0014 2a ret

    With the advent of dotnet/coreclr#14420 the jit will now optimize away the isinst, but the box cleanup opts for unbox.any don't fire because there is usually a temp in the way, and so we generate the following code for Main:

    G_M4930_IG01:57pushrdi56pushrsi 4883EC28 subrsp,40G_M4930_IG02:; ** BOX (y) ** 48B9086014E2FA7F0000 movrcx,0x7FFAE2146008 E8BB3C835F call CORINFO_HELP_NEWSFAST  C7400804000000 mov dword ptr [rax+8],4 488BF0 movrsi,rax 4885F6 testrsi,rsi ; gratuitous null check ?7504jne SHORT G_M4930_IG03 33FF xoredi,edi EB2D jmp SHORT G_M4930_IG05G_M4930_IG03:; * UNBOX.ANY type check 48BA086014E2FA7F0000 movrdx,0x7FFAE2146008483916cmp qword ptr [rsi],rdx7412je SHORT G_M4930_IG04; * call helper if type check fails (which it won't) 488BD6 movrdx,rsi 48B9086014E2FA7F0000 movrcx,0x7FFAE2146008 E8470B395F call CORINFO_HELP_UNBOXG_M4930_IG04: 837E0803 cmp dword ptr [rsi+8],3 400F94C7 sete dil 400FB6FF movzxrdi, dilG_M4930_IG05: 85FF testedi,edi 750C jne SHORT G_M4930_IG07 B864000000 moveax,100G_M4930_IG06: 4883C428 addrsp,40 5E poprsi 5F poprdi C3 retG_M4930_IG07: 33C0 xoreax,eaxG_M4930_IG08: 4883C428 addrsp,40 5E poprsi 5F poprdi C3 ret

    If when optimizing a successful cast we copy the result to a new more strongly typed temp (see #9117) we might be able to optimize away the type equality check in the downstream unbox.any (see dotnet/coreclr#14473). And perhaps if we are lucky and the box is simple we might be able to propagate the value to be boxed through the box/unbox to the ultimate use, and so not need the unbox. But the box would remain as it is difficult to remove unless it is known to be dead and whatever transformation makes it dead explicitly cleans it up.

    A couple of ways we could approach this:

    • The optimizer should be able to reason about and propagate boxes and perhaps trigger the box/unbox.any peephole, and turn the result into a simple copy.
    • BOX is just an expression "wrapper" in the spirit of JIT: some ideas on high-level representation of runtime operations in IR #9056. So we could allow the inliner to give BOX(y) the same treatment as y and duplicate it within the inlinee body (essentially, generalize the logic in impInlineFetchArg that begins with else if (argInfo.argIsLclVar && !argCanBeModified) to also apply to BOX(y)). If we added suitable "reference counting" to boxes to track the duplicates then optimizing away the last use of the box could trigger the box cleanup. We have this today but the reference count is implicit and always = 1 since we don't duplicate the boxed values.

    If all this kicked in, the code for Main above would collapse to simply returning a constant.

    category:cq
    theme:importer
    skill-level:expert
    cost:medium

    Metadata

    Metadata

    Labels

    Priority:2Work that is important, but not critical for the releasearea-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMIenhancementProduct code improvement that does NOT require public API changes/additionsoptimizationtenet-performancePerformance related issue

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

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

      JIT: optimizations for multi-use boxes #9118

      Description

      @AndyAyersMS

      A fairly common pattern (especially after inlining) is to see a box that feeds an isinst and if that succeeds, an unbox.any. For example:

      usingSystem;usingSystem.Collections.Generic;usingSystem.Runtime.CompilerServices;internalclassObjectEqualityComparer<T>:EqualityComparer<T>{[MethodImpl(MethodImplOptions.AggressiveInlining)]publicoverrideboolEquals(Tx,Ty){if(x!=null){if(y!=null)returnx.Equals(y);returnfalse;}if(y!=null)returnfalse;returntrue;}[MethodImpl(MethodImplOptions.AggressiveInlining)]publicoverrideintGetHashCode(Tobj)=>obj?.GetHashCode()??0;// Equals method for the comparer itself.publicoverrideboolEquals(Objectobj)=>obj!=null&&GetType()==obj.GetType();publicoverrideintGetHashCode()=>GetType().GetHashCode();}classC{publicstaticintMain(){varcomp=newObjectEqualityComparer<int>();boolresult=comp.Equals(3,4);returnresult?0:100;}}

      We get pretty far when optimizing Main here -- we can devirtualize the call to Equals, inline it and remove the null checks since we have a value type, then inline the inner call to Equals. But along the way we have to box y and the inner Equals has the following IL:

      IL_0000 03ldarg.1 IL_0001 75 f1 000002isinst0x20000F1
      IL_0006 2d 02brtrue.s2 (IL_000a)
      IL_0008 16ldc.i4.0 IL_0009 2a ret IL_000a 02ldarg.0 IL_000b 4a ldind.i4 IL_000c 03ldarg.1 IL_000d a5 f1 000002unbox.any0x20000F1
      IL_0012 fe 01ceq IL_0014 2a ret

      With the advent of dotnet/coreclr#14420 the jit will now optimize away the isinst, but the box cleanup opts for unbox.any don't fire because there is usually a temp in the way, and so we generate the following code for Main:

      G_M4930_IG01:57pushrdi56pushrsi 4883EC28 subrsp,40G_M4930_IG02:; ** BOX (y) ** 48B9086014E2FA7F0000 movrcx,0x7FFAE2146008 E8BB3C835F call CORINFO_HELP_NEWSFAST  C7400804000000 mov dword ptr [rax+8],4 488BF0 movrsi,rax 4885F6 testrsi,rsi ; gratuitous null check ?7504jne SHORT G_M4930_IG03 33FF xoredi,edi EB2D jmp SHORT G_M4930_IG05G_M4930_IG03:; * UNBOX.ANY type check 48BA086014E2FA7F0000 movrdx,0x7FFAE2146008483916cmp qword ptr [rsi],rdx7412je SHORT G_M4930_IG04; * call helper if type check fails (which it won't) 488BD6 movrdx,rsi 48B9086014E2FA7F0000 movrcx,0x7FFAE2146008 E8470B395F call CORINFO_HELP_UNBOXG_M4930_IG04: 837E0803 cmp dword ptr [rsi+8],3 400F94C7 sete dil 400FB6FF movzxrdi, dilG_M4930_IG05: 85FF testedi,edi 750C jne SHORT G_M4930_IG07 B864000000 moveax,100G_M4930_IG06: 4883C428 addrsp,40 5E poprsi 5F poprdi C3 retG_M4930_IG07: 33C0 xoreax,eaxG_M4930_IG08: 4883C428 addrsp,40 5E poprsi 5F poprdi C3 ret

      If when optimizing a successful cast we copy the result to a new more strongly typed temp (see #9117) we might be able to optimize away the type equality check in the downstream unbox.any (see dotnet/coreclr#14473). And perhaps if we are lucky and the box is simple we might be able to propagate the value to be boxed through the box/unbox to the ultimate use, and so not need the unbox. But the box would remain as it is difficult to remove unless it is known to be dead and whatever transformation makes it dead explicitly cleans it up.

      A couple of ways we could approach this:

      • The optimizer should be able to reason about and propagate boxes and perhaps trigger the box/unbox.any peephole, and turn the result into a simple copy.
      • BOX is just an expression "wrapper" in the spirit of JIT: some ideas on high-level representation of runtime operations in IR #9056. So we could allow the inliner to give BOX(y) the same treatment as y and duplicate it within the inlinee body (essentially, generalize the logic in impInlineFetchArg that begins with else if (argInfo.argIsLclVar && !argCanBeModified) to also apply to BOX(y)). If we added suitable "reference counting" to boxes to track the duplicates then optimizing away the last use of the box could trigger the box cleanup. We have this today but the reference count is implicit and always = 1 since we don't duplicate the boxed values.

      If all this kicked in, the code for Main above would collapse to simply returning a constant.

      category:cq
      theme:importer
      skill-level:expert
      cost:medium

      Metadata

      Metadata

      Labels

      Priority:2Work that is important, but not critical for the releasearea-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMIenhancementProduct code improvement that does NOT require public API changes/additionsoptimizationtenet-performancePerformance related issue

      Type

      No type

      Projects

      No projects

        Relationships

        None yet

        Development

        No branches or pull requests

        Issue actions

        , 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', '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('^' + ".*" + '
        Skip to content

        JIT: optimizations for multi-use boxes #9118

        Description

        @AndyAyersMS

        A fairly common pattern (especially after inlining) is to see a box that feeds an isinst and if that succeeds, an unbox.any. For example:

        usingSystem;usingSystem.Collections.Generic;usingSystem.Runtime.CompilerServices;internalclassObjectEqualityComparer<T>:EqualityComparer<T>{[MethodImpl(MethodImplOptions.AggressiveInlining)]publicoverrideboolEquals(Tx,Ty){if(x!=null){if(y!=null)returnx.Equals(y);returnfalse;}if(y!=null)returnfalse;returntrue;}[MethodImpl(MethodImplOptions.AggressiveInlining)]publicoverrideintGetHashCode(Tobj)=>obj?.GetHashCode()??0;// Equals method for the comparer itself.publicoverrideboolEquals(Objectobj)=>obj!=null&&GetType()==obj.GetType();publicoverrideintGetHashCode()=>GetType().GetHashCode();}classC{publicstaticintMain(){varcomp=newObjectEqualityComparer<int>();boolresult=comp.Equals(3,4);returnresult?0:100;}}

        We get pretty far when optimizing Main here -- we can devirtualize the call to Equals, inline it and remove the null checks since we have a value type, then inline the inner call to Equals. But along the way we have to box y and the inner Equals has the following IL:

        IL_0000 03ldarg.1 IL_0001 75 f1 000002isinst0x20000F1
        IL_0006 2d 02brtrue.s2 (IL_000a)
        IL_0008 16ldc.i4.0 IL_0009 2a ret IL_000a 02ldarg.0 IL_000b 4a ldind.i4 IL_000c 03ldarg.1 IL_000d a5 f1 000002unbox.any0x20000F1
        IL_0012 fe 01ceq IL_0014 2a ret

        With the advent of dotnet/coreclr#14420 the jit will now optimize away the isinst, but the box cleanup opts for unbox.any don't fire because there is usually a temp in the way, and so we generate the following code for Main:

        G_M4930_IG01:57pushrdi56pushrsi 4883EC28 subrsp,40G_M4930_IG02:; ** BOX (y) ** 48B9086014E2FA7F0000 movrcx,0x7FFAE2146008 E8BB3C835F call CORINFO_HELP_NEWSFAST  C7400804000000 mov dword ptr [rax+8],4 488BF0 movrsi,rax 4885F6 testrsi,rsi ; gratuitous null check ?7504jne SHORT G_M4930_IG03 33FF xoredi,edi EB2D jmp SHORT G_M4930_IG05G_M4930_IG03:; * UNBOX.ANY type check 48BA086014E2FA7F0000 movrdx,0x7FFAE2146008483916cmp qword ptr [rsi],rdx7412je SHORT G_M4930_IG04; * call helper if type check fails (which it won't) 488BD6 movrdx,rsi 48B9086014E2FA7F0000 movrcx,0x7FFAE2146008 E8470B395F call CORINFO_HELP_UNBOXG_M4930_IG04: 837E0803 cmp dword ptr [rsi+8],3 400F94C7 sete dil 400FB6FF movzxrdi, dilG_M4930_IG05: 85FF testedi,edi 750C jne SHORT G_M4930_IG07 B864000000 moveax,100G_M4930_IG06: 4883C428 addrsp,40 5E poprsi 5F poprdi C3 retG_M4930_IG07: 33C0 xoreax,eaxG_M4930_IG08: 4883C428 addrsp,40 5E poprsi 5F poprdi C3 ret

        If when optimizing a successful cast we copy the result to a new more strongly typed temp (see #9117) we might be able to optimize away the type equality check in the downstream unbox.any (see dotnet/coreclr#14473). And perhaps if we are lucky and the box is simple we might be able to propagate the value to be boxed through the box/unbox to the ultimate use, and so not need the unbox. But the box would remain as it is difficult to remove unless it is known to be dead and whatever transformation makes it dead explicitly cleans it up.

        A couple of ways we could approach this:

        • The optimizer should be able to reason about and propagate boxes and perhaps trigger the box/unbox.any peephole, and turn the result into a simple copy.
        • BOX is just an expression "wrapper" in the spirit of JIT: some ideas on high-level representation of runtime operations in IR #9056. So we could allow the inliner to give BOX(y) the same treatment as y and duplicate it within the inlinee body (essentially, generalize the logic in impInlineFetchArg that begins with else if (argInfo.argIsLclVar && !argCanBeModified) to also apply to BOX(y)). If we added suitable "reference counting" to boxes to track the duplicates then optimizing away the last use of the box could trigger the box cleanup. We have this today but the reference count is implicit and always = 1 since we don't duplicate the boxed values.

        If all this kicked in, the code for Main above would collapse to simply returning a constant.

        category:cq
        theme:importer
        skill-level:expert
        cost:medium

        Metadata

        Metadata

        Labels

        Priority:2Work that is important, but not critical for the releasearea-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMIenhancementProduct code improvement that does NOT require public API changes/additionsoptimizationtenet-performancePerformance related issue

        Type

        No type

        Projects

        No projects

          Relationships

          None yet

          Development

          No branches or pull requests

          Issue actions

          , '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" + '
          Skip to content

          JIT: optimizations for multi-use boxes #9118

          Description

          @AndyAyersMS

          A fairly common pattern (especially after inlining) is to see a box that feeds an isinst and if that succeeds, an unbox.any. For example:

          usingSystem;usingSystem.Collections.Generic;usingSystem.Runtime.CompilerServices;internalclassObjectEqualityComparer<T>:EqualityComparer<T>{[MethodImpl(MethodImplOptions.AggressiveInlining)]publicoverrideboolEquals(Tx,Ty){if(x!=null){if(y!=null)returnx.Equals(y);returnfalse;}if(y!=null)returnfalse;returntrue;}[MethodImpl(MethodImplOptions.AggressiveInlining)]publicoverrideintGetHashCode(Tobj)=>obj?.GetHashCode()??0;// Equals method for the comparer itself.publicoverrideboolEquals(Objectobj)=>obj!=null&&GetType()==obj.GetType();publicoverrideintGetHashCode()=>GetType().GetHashCode();}classC{publicstaticintMain(){varcomp=newObjectEqualityComparer<int>();boolresult=comp.Equals(3,4);returnresult?0:100;}}

          We get pretty far when optimizing Main here -- we can devirtualize the call to Equals, inline it and remove the null checks since we have a value type, then inline the inner call to Equals. But along the way we have to box y and the inner Equals has the following IL:

          IL_0000 03ldarg.1 IL_0001 75 f1 000002isinst0x20000F1
          IL_0006 2d 02brtrue.s2 (IL_000a)
          IL_0008 16ldc.i4.0 IL_0009 2a ret IL_000a 02ldarg.0 IL_000b 4a ldind.i4 IL_000c 03ldarg.1 IL_000d a5 f1 000002unbox.any0x20000F1
          IL_0012 fe 01ceq IL_0014 2a ret

          With the advent of dotnet/coreclr#14420 the jit will now optimize away the isinst, but the box cleanup opts for unbox.any don't fire because there is usually a temp in the way, and so we generate the following code for Main:

          G_M4930_IG01:57pushrdi56pushrsi 4883EC28 subrsp,40G_M4930_IG02:; ** BOX (y) ** 48B9086014E2FA7F0000 movrcx,0x7FFAE2146008 E8BB3C835F call CORINFO_HELP_NEWSFAST  C7400804000000 mov dword ptr [rax+8],4 488BF0 movrsi,rax 4885F6 testrsi,rsi ; gratuitous null check ?7504jne SHORT G_M4930_IG03 33FF xoredi,edi EB2D jmp SHORT G_M4930_IG05G_M4930_IG03:; * UNBOX.ANY type check 48BA086014E2FA7F0000 movrdx,0x7FFAE2146008483916cmp qword ptr [rsi],rdx7412je SHORT G_M4930_IG04; * call helper if type check fails (which it won't) 488BD6 movrdx,rsi 48B9086014E2FA7F0000 movrcx,0x7FFAE2146008 E8470B395F call CORINFO_HELP_UNBOXG_M4930_IG04: 837E0803 cmp dword ptr [rsi+8],3 400F94C7 sete dil 400FB6FF movzxrdi, dilG_M4930_IG05: 85FF testedi,edi 750C jne SHORT G_M4930_IG07 B864000000 moveax,100G_M4930_IG06: 4883C428 addrsp,40 5E poprsi 5F poprdi C3 retG_M4930_IG07: 33C0 xoreax,eaxG_M4930_IG08: 4883C428 addrsp,40 5E poprsi 5F poprdi C3 ret

          If when optimizing a successful cast we copy the result to a new more strongly typed temp (see #9117) we might be able to optimize away the type equality check in the downstream unbox.any (see dotnet/coreclr#14473). And perhaps if we are lucky and the box is simple we might be able to propagate the value to be boxed through the box/unbox to the ultimate use, and so not need the unbox. But the box would remain as it is difficult to remove unless it is known to be dead and whatever transformation makes it dead explicitly cleans it up.

          A couple of ways we could approach this:

          • The optimizer should be able to reason about and propagate boxes and perhaps trigger the box/unbox.any peephole, and turn the result into a simple copy.
          • BOX is just an expression "wrapper" in the spirit of JIT: some ideas on high-level representation of runtime operations in IR #9056. So we could allow the inliner to give BOX(y) the same treatment as y and duplicate it within the inlinee body (essentially, generalize the logic in impInlineFetchArg that begins with else if (argInfo.argIsLclVar && !argCanBeModified) to also apply to BOX(y)). If we added suitable "reference counting" to boxes to track the duplicates then optimizing away the last use of the box could trigger the box cleanup. We have this today but the reference count is implicit and always = 1 since we don't duplicate the boxed values.

          If all this kicked in, the code for Main above would collapse to simply returning a constant.

          category:cq
          theme:importer
          skill-level:expert
          cost:medium

          Metadata

          Metadata

          Labels

          Priority:2Work that is important, but not critical for the releasearea-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMIenhancementProduct code improvement that does NOT require public API changes/additionsoptimizationtenet-performancePerformance related issue

          Type

          No type

          Projects

          No projects

            Relationships

            None yet

            Development

            No branches or pull requests

            Issue actions

            , '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('^' + ".*" + '
            Skip to content

            JIT: optimizations for multi-use boxes #9118

            Description

            @AndyAyersMS

            A fairly common pattern (especially after inlining) is to see a box that feeds an isinst and if that succeeds, an unbox.any. For example:

            usingSystem;usingSystem.Collections.Generic;usingSystem.Runtime.CompilerServices;internalclassObjectEqualityComparer<T>:EqualityComparer<T>{[MethodImpl(MethodImplOptions.AggressiveInlining)]publicoverrideboolEquals(Tx,Ty){if(x!=null){if(y!=null)returnx.Equals(y);returnfalse;}if(y!=null)returnfalse;returntrue;}[MethodImpl(MethodImplOptions.AggressiveInlining)]publicoverrideintGetHashCode(Tobj)=>obj?.GetHashCode()??0;// Equals method for the comparer itself.publicoverrideboolEquals(Objectobj)=>obj!=null&&GetType()==obj.GetType();publicoverrideintGetHashCode()=>GetType().GetHashCode();}classC{publicstaticintMain(){varcomp=newObjectEqualityComparer<int>();boolresult=comp.Equals(3,4);returnresult?0:100;}}

            We get pretty far when optimizing Main here -- we can devirtualize the call to Equals, inline it and remove the null checks since we have a value type, then inline the inner call to Equals. But along the way we have to box y and the inner Equals has the following IL:

            IL_0000 03ldarg.1 IL_0001 75 f1 000002isinst0x20000F1
            IL_0006 2d 02brtrue.s2 (IL_000a)
            IL_0008 16ldc.i4.0 IL_0009 2a ret IL_000a 02ldarg.0 IL_000b 4a ldind.i4 IL_000c 03ldarg.1 IL_000d a5 f1 000002unbox.any0x20000F1
            IL_0012 fe 01ceq IL_0014 2a ret

            With the advent of dotnet/coreclr#14420 the jit will now optimize away the isinst, but the box cleanup opts for unbox.any don't fire because there is usually a temp in the way, and so we generate the following code for Main:

            G_M4930_IG01:57pushrdi56pushrsi 4883EC28 subrsp,40G_M4930_IG02:; ** BOX (y) ** 48B9086014E2FA7F0000 movrcx,0x7FFAE2146008 E8BB3C835F call CORINFO_HELP_NEWSFAST  C7400804000000 mov dword ptr [rax+8],4 488BF0 movrsi,rax 4885F6 testrsi,rsi ; gratuitous null check ?7504jne SHORT G_M4930_IG03 33FF xoredi,edi EB2D jmp SHORT G_M4930_IG05G_M4930_IG03:; * UNBOX.ANY type check 48BA086014E2FA7F0000 movrdx,0x7FFAE2146008483916cmp qword ptr [rsi],rdx7412je SHORT G_M4930_IG04; * call helper if type check fails (which it won't) 488BD6 movrdx,rsi 48B9086014E2FA7F0000 movrcx,0x7FFAE2146008 E8470B395F call CORINFO_HELP_UNBOXG_M4930_IG04: 837E0803 cmp dword ptr [rsi+8],3 400F94C7 sete dil 400FB6FF movzxrdi, dilG_M4930_IG05: 85FF testedi,edi 750C jne SHORT G_M4930_IG07 B864000000 moveax,100G_M4930_IG06: 4883C428 addrsp,40 5E poprsi 5F poprdi C3 retG_M4930_IG07: 33C0 xoreax,eaxG_M4930_IG08: 4883C428 addrsp,40 5E poprsi 5F poprdi C3 ret

            If when optimizing a successful cast we copy the result to a new more strongly typed temp (see #9117) we might be able to optimize away the type equality check in the downstream unbox.any (see dotnet/coreclr#14473). And perhaps if we are lucky and the box is simple we might be able to propagate the value to be boxed through the box/unbox to the ultimate use, and so not need the unbox. But the box would remain as it is difficult to remove unless it is known to be dead and whatever transformation makes it dead explicitly cleans it up.

            A couple of ways we could approach this:

            • The optimizer should be able to reason about and propagate boxes and perhaps trigger the box/unbox.any peephole, and turn the result into a simple copy.
            • BOX is just an expression "wrapper" in the spirit of JIT: some ideas on high-level representation of runtime operations in IR #9056. So we could allow the inliner to give BOX(y) the same treatment as y and duplicate it within the inlinee body (essentially, generalize the logic in impInlineFetchArg that begins with else if (argInfo.argIsLclVar && !argCanBeModified) to also apply to BOX(y)). If we added suitable "reference counting" to boxes to track the duplicates then optimizing away the last use of the box could trigger the box cleanup. We have this today but the reference count is implicit and always = 1 since we don't duplicate the boxed values.

            If all this kicked in, the code for Main above would collapse to simply returning a constant.

            category:cq
            theme:importer
            skill-level:expert
            cost:medium

            Metadata

            Metadata

            Labels

            Priority:2Work that is important, but not critical for the releasearea-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMIenhancementProduct code improvement that does NOT require public API changes/additionsoptimizationtenet-performancePerformance related issue

            Type

            No type

            Projects

            No projects

              Relationships

              None yet

              Development

              No branches or pull requests

              Issue actions

              , '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); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
              Skip to content

              JIT: optimizations for multi-use boxes #9118

              Description

              @AndyAyersMS

              A fairly common pattern (especially after inlining) is to see a box that feeds an isinst and if that succeeds, an unbox.any. For example:

              usingSystem;usingSystem.Collections.Generic;usingSystem.Runtime.CompilerServices;internalclassObjectEqualityComparer<T>:EqualityComparer<T>{[MethodImpl(MethodImplOptions.AggressiveInlining)]publicoverrideboolEquals(Tx,Ty){if(x!=null){if(y!=null)returnx.Equals(y);returnfalse;}if(y!=null)returnfalse;returntrue;}[MethodImpl(MethodImplOptions.AggressiveInlining)]publicoverrideintGetHashCode(Tobj)=>obj?.GetHashCode()??0;// Equals method for the comparer itself.publicoverrideboolEquals(Objectobj)=>obj!=null&&GetType()==obj.GetType();publicoverrideintGetHashCode()=>GetType().GetHashCode();}classC{publicstaticintMain(){varcomp=newObjectEqualityComparer<int>();boolresult=comp.Equals(3,4);returnresult?0:100;}}

              We get pretty far when optimizing Main here -- we can devirtualize the call to Equals, inline it and remove the null checks since we have a value type, then inline the inner call to Equals. But along the way we have to box y and the inner Equals has the following IL:

              IL_0000 03ldarg.1 IL_0001 75 f1 000002isinst0x20000F1
              IL_0006 2d 02brtrue.s2 (IL_000a)
              IL_0008 16ldc.i4.0 IL_0009 2a ret IL_000a 02ldarg.0 IL_000b 4a ldind.i4 IL_000c 03ldarg.1 IL_000d a5 f1 000002unbox.any0x20000F1
              IL_0012 fe 01ceq IL_0014 2a ret

              With the advent of dotnet/coreclr#14420 the jit will now optimize away the isinst, but the box cleanup opts for unbox.any don't fire because there is usually a temp in the way, and so we generate the following code for Main:

              G_M4930_IG01:57pushrdi56pushrsi 4883EC28 subrsp,40G_M4930_IG02:; ** BOX (y) ** 48B9086014E2FA7F0000 movrcx,0x7FFAE2146008 E8BB3C835F call CORINFO_HELP_NEWSFAST  C7400804000000 mov dword ptr [rax+8],4 488BF0 movrsi,rax 4885F6 testrsi,rsi ; gratuitous null check ?7504jne SHORT G_M4930_IG03 33FF xoredi,edi EB2D jmp SHORT G_M4930_IG05G_M4930_IG03:; * UNBOX.ANY type check 48BA086014E2FA7F0000 movrdx,0x7FFAE2146008483916cmp qword ptr [rsi],rdx7412je SHORT G_M4930_IG04; * call helper if type check fails (which it won't) 488BD6 movrdx,rsi 48B9086014E2FA7F0000 movrcx,0x7FFAE2146008 E8470B395F call CORINFO_HELP_UNBOXG_M4930_IG04: 837E0803 cmp dword ptr [rsi+8],3 400F94C7 sete dil 400FB6FF movzxrdi, dilG_M4930_IG05: 85FF testedi,edi 750C jne SHORT G_M4930_IG07 B864000000 moveax,100G_M4930_IG06: 4883C428 addrsp,40 5E poprsi 5F poprdi C3 retG_M4930_IG07: 33C0 xoreax,eaxG_M4930_IG08: 4883C428 addrsp,40 5E poprsi 5F poprdi C3 ret

              If when optimizing a successful cast we copy the result to a new more strongly typed temp (see #9117) we might be able to optimize away the type equality check in the downstream unbox.any (see dotnet/coreclr#14473). And perhaps if we are lucky and the box is simple we might be able to propagate the value to be boxed through the box/unbox to the ultimate use, and so not need the unbox. But the box would remain as it is difficult to remove unless it is known to be dead and whatever transformation makes it dead explicitly cleans it up.

              A couple of ways we could approach this:

              • The optimizer should be able to reason about and propagate boxes and perhaps trigger the box/unbox.any peephole, and turn the result into a simple copy.
              • BOX is just an expression "wrapper" in the spirit of JIT: some ideas on high-level representation of runtime operations in IR #9056. So we could allow the inliner to give BOX(y) the same treatment as y and duplicate it within the inlinee body (essentially, generalize the logic in impInlineFetchArg that begins with else if (argInfo.argIsLclVar && !argCanBeModified) to also apply to BOX(y)). If we added suitable "reference counting" to boxes to track the duplicates then optimizing away the last use of the box could trigger the box cleanup. We have this today but the reference count is implicit and always = 1 since we don't duplicate the boxed values.

              If all this kicked in, the code for Main above would collapse to simply returning a constant.

              category:cq
              theme:importer
              skill-level:expert
              cost:medium

              Metadata

              Metadata

              Labels

              Priority:2Work that is important, but not critical for the releasearea-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMIenhancementProduct code improvement that does NOT require public API changes/additionsoptimizationtenet-performancePerformance related issue

              Type

              No type

              Projects

              No projects

                Relationships

                None yet

                Development

                No branches or pull requests

                Issue actions

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

                JIT: optimizations for multi-use boxes #9118

                Description

                @AndyAyersMS

                A fairly common pattern (especially after inlining) is to see a box that feeds an isinst and if that succeeds, an unbox.any. For example:

                usingSystem;usingSystem.Collections.Generic;usingSystem.Runtime.CompilerServices;internalclassObjectEqualityComparer<T>:EqualityComparer<T>{[MethodImpl(MethodImplOptions.AggressiveInlining)]publicoverrideboolEquals(Tx,Ty){if(x!=null){if(y!=null)returnx.Equals(y);returnfalse;}if(y!=null)returnfalse;returntrue;}[MethodImpl(MethodImplOptions.AggressiveInlining)]publicoverrideintGetHashCode(Tobj)=>obj?.GetHashCode()??0;// Equals method for the comparer itself.publicoverrideboolEquals(Objectobj)=>obj!=null&&GetType()==obj.GetType();publicoverrideintGetHashCode()=>GetType().GetHashCode();}classC{publicstaticintMain(){varcomp=newObjectEqualityComparer<int>();boolresult=comp.Equals(3,4);returnresult?0:100;}}

                We get pretty far when optimizing Main here -- we can devirtualize the call to Equals, inline it and remove the null checks since we have a value type, then inline the inner call to Equals. But along the way we have to box y and the inner Equals has the following IL:

                IL_0000 03ldarg.1 IL_0001 75 f1 000002isinst0x20000F1
                IL_0006 2d 02brtrue.s2 (IL_000a)
                IL_0008 16ldc.i4.0 IL_0009 2a ret IL_000a 02ldarg.0 IL_000b 4a ldind.i4 IL_000c 03ldarg.1 IL_000d a5 f1 000002unbox.any0x20000F1
                IL_0012 fe 01ceq IL_0014 2a ret

                With the advent of dotnet/coreclr#14420 the jit will now optimize away the isinst, but the box cleanup opts for unbox.any don't fire because there is usually a temp in the way, and so we generate the following code for Main:

                G_M4930_IG01:57pushrdi56pushrsi 4883EC28 subrsp,40G_M4930_IG02:; ** BOX (y) ** 48B9086014E2FA7F0000 movrcx,0x7FFAE2146008 E8BB3C835F call CORINFO_HELP_NEWSFAST  C7400804000000 mov dword ptr [rax+8],4 488BF0 movrsi,rax 4885F6 testrsi,rsi ; gratuitous null check ?7504jne SHORT G_M4930_IG03 33FF xoredi,edi EB2D jmp SHORT G_M4930_IG05G_M4930_IG03:; * UNBOX.ANY type check 48BA086014E2FA7F0000 movrdx,0x7FFAE2146008483916cmp qword ptr [rsi],rdx7412je SHORT G_M4930_IG04; * call helper if type check fails (which it won't) 488BD6 movrdx,rsi 48B9086014E2FA7F0000 movrcx,0x7FFAE2146008 E8470B395F call CORINFO_HELP_UNBOXG_M4930_IG04: 837E0803 cmp dword ptr [rsi+8],3 400F94C7 sete dil 400FB6FF movzxrdi, dilG_M4930_IG05: 85FF testedi,edi 750C jne SHORT G_M4930_IG07 B864000000 moveax,100G_M4930_IG06: 4883C428 addrsp,40 5E poprsi 5F poprdi C3 retG_M4930_IG07: 33C0 xoreax,eaxG_M4930_IG08: 4883C428 addrsp,40 5E poprsi 5F poprdi C3 ret

                If when optimizing a successful cast we copy the result to a new more strongly typed temp (see #9117) we might be able to optimize away the type equality check in the downstream unbox.any (see dotnet/coreclr#14473). And perhaps if we are lucky and the box is simple we might be able to propagate the value to be boxed through the box/unbox to the ultimate use, and so not need the unbox. But the box would remain as it is difficult to remove unless it is known to be dead and whatever transformation makes it dead explicitly cleans it up.

                A couple of ways we could approach this:

                • The optimizer should be able to reason about and propagate boxes and perhaps trigger the box/unbox.any peephole, and turn the result into a simple copy.
                • BOX is just an expression "wrapper" in the spirit of JIT: some ideas on high-level representation of runtime operations in IR #9056. So we could allow the inliner to give BOX(y) the same treatment as y and duplicate it within the inlinee body (essentially, generalize the logic in impInlineFetchArg that begins with else if (argInfo.argIsLclVar && !argCanBeModified) to also apply to BOX(y)). If we added suitable "reference counting" to boxes to track the duplicates then optimizing away the last use of the box could trigger the box cleanup. We have this today but the reference count is implicit and always = 1 since we don't duplicate the boxed values.

                If all this kicked in, the code for Main above would collapse to simply returning a constant.

                category:cq
                theme:importer
                skill-level:expert
                cost:medium

                Metadata

                Metadata

                Labels

                Priority:2Work that is important, but not critical for the releasearea-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMIenhancementProduct code improvement that does NOT require public API changes/additionsoptimizationtenet-performancePerformance related issue

                Type

                No type

                Projects

                No projects

                  Relationships

                  None yet

                  Development

                  No branches or pull requests

                  Issue actions