[JIT] X64 - Extend emitter peephole optimization of eliminating unnecessary mov instructions - #79381

Merged
TIHan merged 88 commits into
dotnet:mainfrom
TIHan:mov-elim-1
Feb 25, 2023
Merged

[JIT] X64 - Extend emitter peephole optimization of eliminating unnecessary mov instructions#79381
TIHan merged 88 commits into
dotnet:mainfrom
TIHan:mov-elim-1

Conversation

@TIHan

@TIHanTIHan commented Dec 8, 2022

Copy link
Copy Markdown
Contributor

Description

Resolves#10315 based on the example code given in the first post.

This will not eliminate all possible unnecessary mov instructions, but it handles more now.

The JIT's emitter already had an existing way of removing the instructions using:

boolemitter::AreUpper32BitsZero(regNumber reg)

But, it was only able to look back at one instruction.

This PR extends AreUpper32BitsZero to allow looking back up to 256 instructions(max limit of instructions for an IG).

Example diffs:

@@ -28,13 +28,9 @@ G_M17551_IG02: ; gcrefRegs=00000000 {}, byrefRegs=00000004 {rdx}, byref
movzx r8, byte ptr [rcx+01H]
movzx r9, byte ptr [rcx+02H]
movzx rcx, byte ptr [rcx+03H]
- mov eax, eax
movsx rax, byte ptr [rdx+rax]
- mov r8d, r8d
movsx r8, byte ptr [rdx+r8]
- mov r9d, r9d
movsx r9, byte ptr [rdx+r9]
- mov ecx, ecx
movsx rdx, byte ptr [rdx+rcx]
; byrRegs -[rdx]
shl eax, 18
@@ -43,12 +39,12 @@ G_M17551_IG02: ; gcrefRegs=00000000 {}, byrefRegs=00000004 {rdx}, byref
or eax, edx
or r8d, r9d
or eax, r8d
- ;; size=66 bbWeight=1 PerfScore 27.25+ ;; size=56 bbWeight=1 PerfScore 26.25
G_M17551_IG03: ; , epilog, nogc, extend
ret ;; size=1 bbWeight=1 PerfScore 1.00
-; Total bytes of code 67, prolog size 0, PerfScore 34.95, instruction count 19, allocated bytes for code 67 (MethodHash=ea74bb70) for method System.Buffers.Text.Base64:Decode(ulong,byref):int+; Total bytes of code 57, prolog size 0, PerfScore 32.95, instruction count 15, allocated bytes for code 57 (MethodHash=ea74bb70) for method System.Buffers.Text.Base64:Decode(ulong,byref):int

Diffs from the issue's example:
image

Acceptance Criteria

  • Disasm test case
  • Comments

@ghostghost added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Dec 8, 2022
@ghostghost assigned TIHanDec 8, 2022
@ghost

ghost commented Dec 8, 2022

Copy link
Copy Markdown

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

Issue Details

Description

Resolves #10315 based on the example code given in the first post.

This will not eliminate all possible unnecessary mov instructions, but it handles a little more than before in common cases.

The JIT's emitter already had an existing way of removing the instructions using:

boolemitter::AreUpper32BitsZero(regNumber reg)

But, it was only able to look back at one instruction.

This PR extends AreUpper32BitsZero to allow looking back at any number of instructions, but only if it's safe to do so. It does not keep a list of instructions, it only keeps a single unsigned int for tracking if a register has its upper 32-bits set to zero:

// IMPORTANT: Only contains information from **before** the last emitted instruction.// A lookup where each bit position corresponds to a register.// A bit that is set means the register's upper 32-bits are zero.// This effectively keeps track of which registers have their upper 32-bits set to zero.// GPRs (general-purpose registers) only.unsignedint upper32BitsZeroRegLookup;

Example diffs:

@@ -28,13 +28,9 @@ G_M17551_IG02: ; gcrefRegs=00000000 {}, byrefRegs=00000004 {rdx}, byref
movzx r8, byte ptr [rcx+01H]
movzx r9, byte ptr [rcx+02H]
movzx rcx, byte ptr [rcx+03H]
- mov eax, eax
movsx rax, byte ptr [rdx+rax]
- mov r8d, r8d
movsx r8, byte ptr [rdx+r8]
- mov r9d, r9d
movsx r9, byte ptr [rdx+r9]
- mov ecx, ecx
movsx rdx, byte ptr [rdx+rcx]
; byrRegs -[rdx]
shl eax, 18
@@ -43,12 +39,12 @@ G_M17551_IG02: ; gcrefRegs=00000000 {}, byrefRegs=00000004 {rdx}, byref
or eax, edx
or r8d, r9d
or eax, r8d
- ;; size=66 bbWeight=1 PerfScore 27.25+ ;; size=56 bbWeight=1 PerfScore 26.25
G_M17551_IG03: ; , epilog, nogc, extend
ret ;; size=1 bbWeight=1 PerfScore 1.00
-; Total bytes of code 67, prolog size 0, PerfScore 34.95, instruction count 19, allocated bytes for code 67 (MethodHash=ea74bb70) for method System.Buffers.Text.Base64:Decode(ulong,byref):int+; Total bytes of code 57, prolog size 0, PerfScore 32.95, instruction count 15, allocated bytes for code 57 (MethodHash=ea74bb70) for method System.Buffers.Text.Base64:Decode(ulong,byref):int

Diffs from the issue's example:
image

Diff Summary

Diffs are based on 1,398,805 contexts (351,415 MinOpts, 1,047,390 FullOpts).

MISSED contexts: base: 20, diff: 20

Overall (-1,124 bytes)
CollectionBase size (bytes)Diff size (bytes)
benchmarks.run.windows.x64.checked.mch25,002,887-124
coreclr_tests.run.windows.x64.checked.mch362,751,515-176
libraries.crossgen2.windows.x64.checked.mch35,246,384-153
libraries.pmi.windows.x64.checked.mch52,033,158-367
libraries_tests.pmi.windows.x64.checked.mch114,343,307-304
FullOpts (-1,124 bytes)
CollectionBase size (bytes)Diff size (bytes)
benchmarks.run.windows.x64.checked.mch23,285,431-124
coreclr_tests.run.windows.x64.checked.mch96,229,781-176
libraries.crossgen2.windows.x64.checked.mch35,245,195-153
libraries.pmi.windows.x64.checked.mch50,532,678-367
libraries_tests.pmi.windows.x64.checked.mch107,461,046-304
Details

Improvements/regressions per collection

CollectionContexts with diffsImprovementsRegressionsImprovements (bytes)Regressions (bytes)
benchmarks.run.windows.x64.checked.mch33300-124+0
coreclr_tests.run.windows.x64.checked.mch49490-176+0
libraries.crossgen2.windows.x64.checked.mch36360-153+0
libraries.pmi.windows.x64.checked.mch1181161-372+5
libraries_tests.pmi.windows.x64.checked.mch100990-304+0
3363301-1,129+5

Context information

CollectionDiffed contextsMinOptsFullOptsMissed, baseMissed, diff
benchmarks.run.windows.x64.checked.mch66,54516,37050,17500
coreclr_tests.run.windows.x64.checked.mch509,739320,923188,81600
libraries.crossgen2.windows.x64.checked.mch216,15415216,13944
libraries.pmi.windows.x64.checked.mch271,0974,958266,13988
libraries_tests.pmi.windows.x64.checked.mch335,2709,149326,12188
1,398,805351,4151,047,3902020

jit-analyze output

Acceptance Criteria

  • Disasm test cases
  • Regression test cases
Author:TIHan
Assignees:-
Labels:

area-CodeGen-coreclr

Milestone:-

{
assert(emitHasLastIns() == (emitLastInsIG != nullptr));

return emitHasLastIns() && // there is an emitLastInstr

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.

Maybe this is for a separate PR, but I think we need to prevent peephole optimizations when we're in the prolog or epilog. I.e.,

 if (emitIGisInProlog(emitCurIG) || emitIGisInEpilog(emitCurIG))
{
return false;
}
#ifdef FEATURE_EH_FUNCLETS
if (emitIGisInFuncletProlog(emitCurIG) || emitIGisInFuncletEpilog(emitCurIG))
{
return false;
}
#endif

There is too much special handling in the prolog/epilog (e.g., unwinding) to allow peeps to kick in. There may be very specific cases where they are ok, but that requires some careful thinking.

@TIHan

Copy link
Copy Markdown
ContributorAuthor

/azp run runtime-coreclr superpmi-diffs

@azure-pipelines

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

@TIHan

Copy link
Copy Markdown
ContributorAuthor

/azp run runtime-coreclr superpmi-replay

@azure-pipelines

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

@TIHan

Copy link
Copy Markdown
ContributorAuthor

/azp run runtime-coreclr gcstress0x3-gcstress0xc

@TIHan

Copy link
Copy Markdown
ContributorAuthor

/azp run jitstress

@azure-pipelines

Copy link
Copy Markdown
No pipelines are associated with this pull request.

@azure-pipelines

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

@TIHan

Copy link
Copy Markdown
ContributorAuthor

/azp run runtime-coreclr jitstress

@azure-pipelines

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

@TIHan

TIHan commented Feb 24, 2023

Copy link
Copy Markdown
ContributorAuthor

@dotnet/jit-contrib @BruceForstall This is ready again. I ran gcstress and jistress, they passed CI. The current failures are unrelated.

@AndyAyersMS

Copy link
Copy Markdown
Member

Is there any way to avoid the TP impact in min opts?

@BruceForstall

Copy link
Copy Markdown
Contributor

Diffs

@BruceForstall

Copy link
Copy Markdown
Contributor

Is there any way to avoid the TP impact in min opts?

I believe it's a result of enabling backwards navigation in the insGroup/instrDesc (#80840), and maintaining those data structures. It's not obvious how this could only be done for non-MinOpts (and whether it would be advisable if it could be done).

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

LGTM

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 SuperPMI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Jit can generate pointless movs

6 participants

@TIHan@BruceForstall@tannergooding@AndyAyersMS@kunalspathak@markples
, '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

[JIT] X64 - Extend emitter peephole optimization of eliminating unnecessary mov instructions - #79381

Merged
TIHan merged 88 commits into
dotnet:mainfrom
TIHan:mov-elim-1
Feb 25, 2023
Merged

[JIT] X64 - Extend emitter peephole optimization of eliminating unnecessary mov instructions#79381
TIHan merged 88 commits into
dotnet:mainfrom
TIHan:mov-elim-1

Conversation

@TIHan

@TIHanTIHan commented Dec 8, 2022

Copy link
Copy Markdown
Contributor

Description

Resolves#10315 based on the example code given in the first post.

This will not eliminate all possible unnecessary mov instructions, but it handles more now.

The JIT's emitter already had an existing way of removing the instructions using:

boolemitter::AreUpper32BitsZero(regNumber reg)

But, it was only able to look back at one instruction.

This PR extends AreUpper32BitsZero to allow looking back up to 256 instructions(max limit of instructions for an IG).

Example diffs:

@@ -28,13 +28,9 @@ G_M17551_IG02: ; gcrefRegs=00000000 {}, byrefRegs=00000004 {rdx}, byref
movzx r8, byte ptr [rcx+01H]
movzx r9, byte ptr [rcx+02H]
movzx rcx, byte ptr [rcx+03H]
- mov eax, eax
movsx rax, byte ptr [rdx+rax]
- mov r8d, r8d
movsx r8, byte ptr [rdx+r8]
- mov r9d, r9d
movsx r9, byte ptr [rdx+r9]
- mov ecx, ecx
movsx rdx, byte ptr [rdx+rcx]
; byrRegs -[rdx]
shl eax, 18
@@ -43,12 +39,12 @@ G_M17551_IG02: ; gcrefRegs=00000000 {}, byrefRegs=00000004 {rdx}, byref
or eax, edx
or r8d, r9d
or eax, r8d
- ;; size=66 bbWeight=1 PerfScore 27.25+ ;; size=56 bbWeight=1 PerfScore 26.25
G_M17551_IG03: ; , epilog, nogc, extend
ret ;; size=1 bbWeight=1 PerfScore 1.00
-; Total bytes of code 67, prolog size 0, PerfScore 34.95, instruction count 19, allocated bytes for code 67 (MethodHash=ea74bb70) for method System.Buffers.Text.Base64:Decode(ulong,byref):int+; Total bytes of code 57, prolog size 0, PerfScore 32.95, instruction count 15, allocated bytes for code 57 (MethodHash=ea74bb70) for method System.Buffers.Text.Base64:Decode(ulong,byref):int

Diffs from the issue's example:
image

Acceptance Criteria

  • Disasm test case
  • Comments

@ghostghost added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Dec 8, 2022
@ghostghost assigned TIHanDec 8, 2022
@ghost

ghost commented Dec 8, 2022

Copy link
Copy Markdown

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

Issue Details

Description

Resolves #10315 based on the example code given in the first post.

This will not eliminate all possible unnecessary mov instructions, but it handles a little more than before in common cases.

The JIT's emitter already had an existing way of removing the instructions using:

boolemitter::AreUpper32BitsZero(regNumber reg)

But, it was only able to look back at one instruction.

This PR extends AreUpper32BitsZero to allow looking back at any number of instructions, but only if it's safe to do so. It does not keep a list of instructions, it only keeps a single unsigned int for tracking if a register has its upper 32-bits set to zero:

// IMPORTANT: Only contains information from **before** the last emitted instruction.// A lookup where each bit position corresponds to a register.// A bit that is set means the register's upper 32-bits are zero.// This effectively keeps track of which registers have their upper 32-bits set to zero.// GPRs (general-purpose registers) only.unsignedint upper32BitsZeroRegLookup;

Example diffs:

@@ -28,13 +28,9 @@ G_M17551_IG02: ; gcrefRegs=00000000 {}, byrefRegs=00000004 {rdx}, byref
movzx r8, byte ptr [rcx+01H]
movzx r9, byte ptr [rcx+02H]
movzx rcx, byte ptr [rcx+03H]
- mov eax, eax
movsx rax, byte ptr [rdx+rax]
- mov r8d, r8d
movsx r8, byte ptr [rdx+r8]
- mov r9d, r9d
movsx r9, byte ptr [rdx+r9]
- mov ecx, ecx
movsx rdx, byte ptr [rdx+rcx]
; byrRegs -[rdx]
shl eax, 18
@@ -43,12 +39,12 @@ G_M17551_IG02: ; gcrefRegs=00000000 {}, byrefRegs=00000004 {rdx}, byref
or eax, edx
or r8d, r9d
or eax, r8d
- ;; size=66 bbWeight=1 PerfScore 27.25+ ;; size=56 bbWeight=1 PerfScore 26.25
G_M17551_IG03: ; , epilog, nogc, extend
ret ;; size=1 bbWeight=1 PerfScore 1.00
-; Total bytes of code 67, prolog size 0, PerfScore 34.95, instruction count 19, allocated bytes for code 67 (MethodHash=ea74bb70) for method System.Buffers.Text.Base64:Decode(ulong,byref):int+; Total bytes of code 57, prolog size 0, PerfScore 32.95, instruction count 15, allocated bytes for code 57 (MethodHash=ea74bb70) for method System.Buffers.Text.Base64:Decode(ulong,byref):int

Diffs from the issue's example:
image

Diff Summary

Diffs are based on 1,398,805 contexts (351,415 MinOpts, 1,047,390 FullOpts).

MISSED contexts: base: 20, diff: 20

Overall (-1,124 bytes)
CollectionBase size (bytes)Diff size (bytes)
benchmarks.run.windows.x64.checked.mch25,002,887-124
coreclr_tests.run.windows.x64.checked.mch362,751,515-176
libraries.crossgen2.windows.x64.checked.mch35,246,384-153
libraries.pmi.windows.x64.checked.mch52,033,158-367
libraries_tests.pmi.windows.x64.checked.mch114,343,307-304
FullOpts (-1,124 bytes)
CollectionBase size (bytes)Diff size (bytes)
benchmarks.run.windows.x64.checked.mch23,285,431-124
coreclr_tests.run.windows.x64.checked.mch96,229,781-176
libraries.crossgen2.windows.x64.checked.mch35,245,195-153
libraries.pmi.windows.x64.checked.mch50,532,678-367
libraries_tests.pmi.windows.x64.checked.mch107,461,046-304
Details

Improvements/regressions per collection

CollectionContexts with diffsImprovementsRegressionsImprovements (bytes)Regressions (bytes)
benchmarks.run.windows.x64.checked.mch33300-124+0
coreclr_tests.run.windows.x64.checked.mch49490-176+0
libraries.crossgen2.windows.x64.checked.mch36360-153+0
libraries.pmi.windows.x64.checked.mch1181161-372+5
libraries_tests.pmi.windows.x64.checked.mch100990-304+0
3363301-1,129+5

Context information

CollectionDiffed contextsMinOptsFullOptsMissed, baseMissed, diff
benchmarks.run.windows.x64.checked.mch66,54516,37050,17500
coreclr_tests.run.windows.x64.checked.mch509,739320,923188,81600
libraries.crossgen2.windows.x64.checked.mch216,15415216,13944
libraries.pmi.windows.x64.checked.mch271,0974,958266,13988
libraries_tests.pmi.windows.x64.checked.mch335,2709,149326,12188
1,398,805351,4151,047,3902020

jit-analyze output

Acceptance Criteria

  • Disasm test cases
  • Regression test cases
Author:TIHan
Assignees:-
Labels:

area-CodeGen-coreclr

Milestone:-

{
assert(emitHasLastIns() == (emitLastInsIG != nullptr));

return emitHasLastIns() && // there is an emitLastInstr

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.

Maybe this is for a separate PR, but I think we need to prevent peephole optimizations when we're in the prolog or epilog. I.e.,

 if (emitIGisInProlog(emitCurIG) || emitIGisInEpilog(emitCurIG))
{
return false;
}
#ifdef FEATURE_EH_FUNCLETS
if (emitIGisInFuncletProlog(emitCurIG) || emitIGisInFuncletEpilog(emitCurIG))
{
return false;
}
#endif

There is too much special handling in the prolog/epilog (e.g., unwinding) to allow peeps to kick in. There may be very specific cases where they are ok, but that requires some careful thinking.

@TIHan

Copy link
Copy Markdown
ContributorAuthor

/azp run runtime-coreclr superpmi-diffs

@azure-pipelines

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

@TIHan

Copy link
Copy Markdown
ContributorAuthor

/azp run runtime-coreclr superpmi-replay

@azure-pipelines

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

@TIHan

Copy link
Copy Markdown
ContributorAuthor

/azp run runtime-coreclr gcstress0x3-gcstress0xc

@TIHan

Copy link
Copy Markdown
ContributorAuthor

/azp run jitstress

@azure-pipelines

Copy link
Copy Markdown
No pipelines are associated with this pull request.

@azure-pipelines

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

@TIHan

Copy link
Copy Markdown
ContributorAuthor

/azp run runtime-coreclr jitstress

@azure-pipelines

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

@TIHan

TIHan commented Feb 24, 2023

Copy link
Copy Markdown
ContributorAuthor

@dotnet/jit-contrib @BruceForstall This is ready again. I ran gcstress and jistress, they passed CI. The current failures are unrelated.

@AndyAyersMS

Copy link
Copy Markdown
Member

Is there any way to avoid the TP impact in min opts?

@BruceForstall

Copy link
Copy Markdown
Contributor

Diffs

@BruceForstall

Copy link
Copy Markdown
Contributor

Is there any way to avoid the TP impact in min opts?

I believe it's a result of enabling backwards navigation in the insGroup/instrDesc (#80840), and maintaining those data structures. It's not obvious how this could only be done for non-MinOpts (and whether it would be advisable if it could be done).

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

LGTM

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 SuperPMI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Jit can generate pointless movs

6 participants

@TIHan@BruceForstall@tannergooding@AndyAyersMS@kunalspathak@markples
, '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

[JIT] X64 - Extend emitter peephole optimization of eliminating unnecessary mov instructions - #79381

Merged
TIHan merged 88 commits into
dotnet:mainfrom
TIHan:mov-elim-1
Feb 25, 2023
Merged

[JIT] X64 - Extend emitter peephole optimization of eliminating unnecessary mov instructions#79381
TIHan merged 88 commits into
dotnet:mainfrom
TIHan:mov-elim-1

Conversation

@TIHan

@TIHanTIHan commented Dec 8, 2022

Copy link
Copy Markdown
Contributor

Description

Resolves#10315 based on the example code given in the first post.

This will not eliminate all possible unnecessary mov instructions, but it handles more now.

The JIT's emitter already had an existing way of removing the instructions using:

boolemitter::AreUpper32BitsZero(regNumber reg)

But, it was only able to look back at one instruction.

This PR extends AreUpper32BitsZero to allow looking back up to 256 instructions(max limit of instructions for an IG).

Example diffs:

@@ -28,13 +28,9 @@ G_M17551_IG02: ; gcrefRegs=00000000 {}, byrefRegs=00000004 {rdx}, byref
movzx r8, byte ptr [rcx+01H]
movzx r9, byte ptr [rcx+02H]
movzx rcx, byte ptr [rcx+03H]
- mov eax, eax
movsx rax, byte ptr [rdx+rax]
- mov r8d, r8d
movsx r8, byte ptr [rdx+r8]
- mov r9d, r9d
movsx r9, byte ptr [rdx+r9]
- mov ecx, ecx
movsx rdx, byte ptr [rdx+rcx]
; byrRegs -[rdx]
shl eax, 18
@@ -43,12 +39,12 @@ G_M17551_IG02: ; gcrefRegs=00000000 {}, byrefRegs=00000004 {rdx}, byref
or eax, edx
or r8d, r9d
or eax, r8d
- ;; size=66 bbWeight=1 PerfScore 27.25+ ;; size=56 bbWeight=1 PerfScore 26.25
G_M17551_IG03: ; , epilog, nogc, extend
ret ;; size=1 bbWeight=1 PerfScore 1.00
-; Total bytes of code 67, prolog size 0, PerfScore 34.95, instruction count 19, allocated bytes for code 67 (MethodHash=ea74bb70) for method System.Buffers.Text.Base64:Decode(ulong,byref):int+; Total bytes of code 57, prolog size 0, PerfScore 32.95, instruction count 15, allocated bytes for code 57 (MethodHash=ea74bb70) for method System.Buffers.Text.Base64:Decode(ulong,byref):int

Diffs from the issue's example:
image

Acceptance Criteria

  • Disasm test case
  • Comments

@ghostghost added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Dec 8, 2022
@ghostghost assigned TIHanDec 8, 2022
@ghost

ghost commented Dec 8, 2022

Copy link
Copy Markdown

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

Issue Details

Description

Resolves #10315 based on the example code given in the first post.

This will not eliminate all possible unnecessary mov instructions, but it handles a little more than before in common cases.

The JIT's emitter already had an existing way of removing the instructions using:

boolemitter::AreUpper32BitsZero(regNumber reg)

But, it was only able to look back at one instruction.

This PR extends AreUpper32BitsZero to allow looking back at any number of instructions, but only if it's safe to do so. It does not keep a list of instructions, it only keeps a single unsigned int for tracking if a register has its upper 32-bits set to zero:

// IMPORTANT: Only contains information from **before** the last emitted instruction.// A lookup where each bit position corresponds to a register.// A bit that is set means the register's upper 32-bits are zero.// This effectively keeps track of which registers have their upper 32-bits set to zero.// GPRs (general-purpose registers) only.unsignedint upper32BitsZeroRegLookup;

Example diffs:

@@ -28,13 +28,9 @@ G_M17551_IG02: ; gcrefRegs=00000000 {}, byrefRegs=00000004 {rdx}, byref
movzx r8, byte ptr [rcx+01H]
movzx r9, byte ptr [rcx+02H]
movzx rcx, byte ptr [rcx+03H]
- mov eax, eax
movsx rax, byte ptr [rdx+rax]
- mov r8d, r8d
movsx r8, byte ptr [rdx+r8]
- mov r9d, r9d
movsx r9, byte ptr [rdx+r9]
- mov ecx, ecx
movsx rdx, byte ptr [rdx+rcx]
; byrRegs -[rdx]
shl eax, 18
@@ -43,12 +39,12 @@ G_M17551_IG02: ; gcrefRegs=00000000 {}, byrefRegs=00000004 {rdx}, byref
or eax, edx
or r8d, r9d
or eax, r8d
- ;; size=66 bbWeight=1 PerfScore 27.25+ ;; size=56 bbWeight=1 PerfScore 26.25
G_M17551_IG03: ; , epilog, nogc, extend
ret ;; size=1 bbWeight=1 PerfScore 1.00
-; Total bytes of code 67, prolog size 0, PerfScore 34.95, instruction count 19, allocated bytes for code 67 (MethodHash=ea74bb70) for method System.Buffers.Text.Base64:Decode(ulong,byref):int+; Total bytes of code 57, prolog size 0, PerfScore 32.95, instruction count 15, allocated bytes for code 57 (MethodHash=ea74bb70) for method System.Buffers.Text.Base64:Decode(ulong,byref):int

Diffs from the issue's example:
image

Diff Summary

Diffs are based on 1,398,805 contexts (351,415 MinOpts, 1,047,390 FullOpts).

MISSED contexts: base: 20, diff: 20

Overall (-1,124 bytes)
CollectionBase size (bytes)Diff size (bytes)
benchmarks.run.windows.x64.checked.mch25,002,887-124
coreclr_tests.run.windows.x64.checked.mch362,751,515-176
libraries.crossgen2.windows.x64.checked.mch35,246,384-153
libraries.pmi.windows.x64.checked.mch52,033,158-367
libraries_tests.pmi.windows.x64.checked.mch114,343,307-304
FullOpts (-1,124 bytes)
CollectionBase size (bytes)Diff size (bytes)
benchmarks.run.windows.x64.checked.mch23,285,431-124
coreclr_tests.run.windows.x64.checked.mch96,229,781-176
libraries.crossgen2.windows.x64.checked.mch35,245,195-153
libraries.pmi.windows.x64.checked.mch50,532,678-367
libraries_tests.pmi.windows.x64.checked.mch107,461,046-304
Details

Improvements/regressions per collection

CollectionContexts with diffsImprovementsRegressionsImprovements (bytes)Regressions (bytes)
benchmarks.run.windows.x64.checked.mch33300-124+0
coreclr_tests.run.windows.x64.checked.mch49490-176+0
libraries.crossgen2.windows.x64.checked.mch36360-153+0
libraries.pmi.windows.x64.checked.mch1181161-372+5
libraries_tests.pmi.windows.x64.checked.mch100990-304+0
3363301-1,129+5

Context information

CollectionDiffed contextsMinOptsFullOptsMissed, baseMissed, diff
benchmarks.run.windows.x64.checked.mch66,54516,37050,17500
coreclr_tests.run.windows.x64.checked.mch509,739320,923188,81600
libraries.crossgen2.windows.x64.checked.mch216,15415216,13944
libraries.pmi.windows.x64.checked.mch271,0974,958266,13988
libraries_tests.pmi.windows.x64.checked.mch335,2709,149326,12188
1,398,805351,4151,047,3902020

jit-analyze output

Acceptance Criteria

  • Disasm test cases
  • Regression test cases
Author:TIHan
Assignees:-
Labels:

area-CodeGen-coreclr

Milestone:-

{
assert(emitHasLastIns() == (emitLastInsIG != nullptr));

return emitHasLastIns() && // there is an emitLastInstr

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.

Maybe this is for a separate PR, but I think we need to prevent peephole optimizations when we're in the prolog or epilog. I.e.,

 if (emitIGisInProlog(emitCurIG) || emitIGisInEpilog(emitCurIG))
{
return false;
}
#ifdef FEATURE_EH_FUNCLETS
if (emitIGisInFuncletProlog(emitCurIG) || emitIGisInFuncletEpilog(emitCurIG))
{
return false;
}
#endif

There is too much special handling in the prolog/epilog (e.g., unwinding) to allow peeps to kick in. There may be very specific cases where they are ok, but that requires some careful thinking.

@TIHan

Copy link
Copy Markdown
ContributorAuthor

/azp run runtime-coreclr superpmi-diffs

@azure-pipelines

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

@TIHan

Copy link
Copy Markdown
ContributorAuthor

/azp run runtime-coreclr superpmi-replay

@azure-pipelines

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

@TIHan

Copy link
Copy Markdown
ContributorAuthor

/azp run runtime-coreclr gcstress0x3-gcstress0xc

@TIHan

Copy link
Copy Markdown
ContributorAuthor

/azp run jitstress

@azure-pipelines

Copy link
Copy Markdown
No pipelines are associated with this pull request.

@azure-pipelines

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

@TIHan

Copy link
Copy Markdown
ContributorAuthor

/azp run runtime-coreclr jitstress

@azure-pipelines

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

@TIHan

TIHan commented Feb 24, 2023

Copy link
Copy Markdown
ContributorAuthor

@dotnet/jit-contrib @BruceForstall This is ready again. I ran gcstress and jistress, they passed CI. The current failures are unrelated.

@AndyAyersMS

Copy link
Copy Markdown
Member

Is there any way to avoid the TP impact in min opts?

@BruceForstall

Copy link
Copy Markdown
Contributor

Diffs

@BruceForstall

Copy link
Copy Markdown
Contributor

Is there any way to avoid the TP impact in min opts?

I believe it's a result of enabling backwards navigation in the insGroup/instrDesc (#80840), and maintaining those data structures. It's not obvious how this could only be done for non-MinOpts (and whether it would be advisable if it could be done).

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

LGTM

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 SuperPMI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Jit can generate pointless movs

6 participants

@TIHan@BruceForstall@tannergooding@AndyAyersMS@kunalspathak@markples
, '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

[JIT] X64 - Extend emitter peephole optimization of eliminating unnecessary mov instructions - #79381

Merged
TIHan merged 88 commits into
dotnet:mainfrom
TIHan:mov-elim-1
Feb 25, 2023
Merged

[JIT] X64 - Extend emitter peephole optimization of eliminating unnecessary mov instructions#79381
TIHan merged 88 commits into
dotnet:mainfrom
TIHan:mov-elim-1

Conversation

@TIHan

@TIHanTIHan commented Dec 8, 2022

Copy link
Copy Markdown
Contributor

Description

Resolves#10315 based on the example code given in the first post.

This will not eliminate all possible unnecessary mov instructions, but it handles more now.

The JIT's emitter already had an existing way of removing the instructions using:

boolemitter::AreUpper32BitsZero(regNumber reg)

But, it was only able to look back at one instruction.

This PR extends AreUpper32BitsZero to allow looking back up to 256 instructions(max limit of instructions for an IG).

Example diffs:

@@ -28,13 +28,9 @@ G_M17551_IG02: ; gcrefRegs=00000000 {}, byrefRegs=00000004 {rdx}, byref
movzx r8, byte ptr [rcx+01H]
movzx r9, byte ptr [rcx+02H]
movzx rcx, byte ptr [rcx+03H]
- mov eax, eax
movsx rax, byte ptr [rdx+rax]
- mov r8d, r8d
movsx r8, byte ptr [rdx+r8]
- mov r9d, r9d
movsx r9, byte ptr [rdx+r9]
- mov ecx, ecx
movsx rdx, byte ptr [rdx+rcx]
; byrRegs -[rdx]
shl eax, 18
@@ -43,12 +39,12 @@ G_M17551_IG02: ; gcrefRegs=00000000 {}, byrefRegs=00000004 {rdx}, byref
or eax, edx
or r8d, r9d
or eax, r8d
- ;; size=66 bbWeight=1 PerfScore 27.25+ ;; size=56 bbWeight=1 PerfScore 26.25
G_M17551_IG03: ; , epilog, nogc, extend
ret ;; size=1 bbWeight=1 PerfScore 1.00
-; Total bytes of code 67, prolog size 0, PerfScore 34.95, instruction count 19, allocated bytes for code 67 (MethodHash=ea74bb70) for method System.Buffers.Text.Base64:Decode(ulong,byref):int+; Total bytes of code 57, prolog size 0, PerfScore 32.95, instruction count 15, allocated bytes for code 57 (MethodHash=ea74bb70) for method System.Buffers.Text.Base64:Decode(ulong,byref):int

Diffs from the issue's example:
image

Acceptance Criteria

  • Disasm test case
  • Comments

@ghostghost added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Dec 8, 2022
@ghostghost assigned TIHanDec 8, 2022
@ghost

ghost commented Dec 8, 2022

Copy link
Copy Markdown

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

Issue Details

Description

Resolves #10315 based on the example code given in the first post.

This will not eliminate all possible unnecessary mov instructions, but it handles a little more than before in common cases.

The JIT's emitter already had an existing way of removing the instructions using:

boolemitter::AreUpper32BitsZero(regNumber reg)

But, it was only able to look back at one instruction.

This PR extends AreUpper32BitsZero to allow looking back at any number of instructions, but only if it's safe to do so. It does not keep a list of instructions, it only keeps a single unsigned int for tracking if a register has its upper 32-bits set to zero:

// IMPORTANT: Only contains information from **before** the last emitted instruction.// A lookup where each bit position corresponds to a register.// A bit that is set means the register's upper 32-bits are zero.// This effectively keeps track of which registers have their upper 32-bits set to zero.// GPRs (general-purpose registers) only.unsignedint upper32BitsZeroRegLookup;

Example diffs:

@@ -28,13 +28,9 @@ G_M17551_IG02: ; gcrefRegs=00000000 {}, byrefRegs=00000004 {rdx}, byref
movzx r8, byte ptr [rcx+01H]
movzx r9, byte ptr [rcx+02H]
movzx rcx, byte ptr [rcx+03H]
- mov eax, eax
movsx rax, byte ptr [rdx+rax]
- mov r8d, r8d
movsx r8, byte ptr [rdx+r8]
- mov r9d, r9d
movsx r9, byte ptr [rdx+r9]
- mov ecx, ecx
movsx rdx, byte ptr [rdx+rcx]
; byrRegs -[rdx]
shl eax, 18
@@ -43,12 +39,12 @@ G_M17551_IG02: ; gcrefRegs=00000000 {}, byrefRegs=00000004 {rdx}, byref
or eax, edx
or r8d, r9d
or eax, r8d
- ;; size=66 bbWeight=1 PerfScore 27.25+ ;; size=56 bbWeight=1 PerfScore 26.25
G_M17551_IG03: ; , epilog, nogc, extend
ret ;; size=1 bbWeight=1 PerfScore 1.00
-; Total bytes of code 67, prolog size 0, PerfScore 34.95, instruction count 19, allocated bytes for code 67 (MethodHash=ea74bb70) for method System.Buffers.Text.Base64:Decode(ulong,byref):int+; Total bytes of code 57, prolog size 0, PerfScore 32.95, instruction count 15, allocated bytes for code 57 (MethodHash=ea74bb70) for method System.Buffers.Text.Base64:Decode(ulong,byref):int

Diffs from the issue's example:
image

Diff Summary

Diffs are based on 1,398,805 contexts (351,415 MinOpts, 1,047,390 FullOpts).

MISSED contexts: base: 20, diff: 20

Overall (-1,124 bytes)
CollectionBase size (bytes)Diff size (bytes)
benchmarks.run.windows.x64.checked.mch25,002,887-124
coreclr_tests.run.windows.x64.checked.mch362,751,515-176
libraries.crossgen2.windows.x64.checked.mch35,246,384-153
libraries.pmi.windows.x64.checked.mch52,033,158-367
libraries_tests.pmi.windows.x64.checked.mch114,343,307-304
FullOpts (-1,124 bytes)
CollectionBase size (bytes)Diff size (bytes)
benchmarks.run.windows.x64.checked.mch23,285,431-124
coreclr_tests.run.windows.x64.checked.mch96,229,781-176
libraries.crossgen2.windows.x64.checked.mch35,245,195-153
libraries.pmi.windows.x64.checked.mch50,532,678-367
libraries_tests.pmi.windows.x64.checked.mch107,461,046-304
Details

Improvements/regressions per collection

CollectionContexts with diffsImprovementsRegressionsImprovements (bytes)Regressions (bytes)
benchmarks.run.windows.x64.checked.mch33300-124+0
coreclr_tests.run.windows.x64.checked.mch49490-176+0
libraries.crossgen2.windows.x64.checked.mch36360-153+0
libraries.pmi.windows.x64.checked.mch1181161-372+5
libraries_tests.pmi.windows.x64.checked.mch100990-304+0
3363301-1,129+5

Context information

CollectionDiffed contextsMinOptsFullOptsMissed, baseMissed, diff
benchmarks.run.windows.x64.checked.mch66,54516,37050,17500
coreclr_tests.run.windows.x64.checked.mch509,739320,923188,81600
libraries.crossgen2.windows.x64.checked.mch216,15415216,13944
libraries.pmi.windows.x64.checked.mch271,0974,958266,13988
libraries_tests.pmi.windows.x64.checked.mch335,2709,149326,12188
1,398,805351,4151,047,3902020

jit-analyze output

Acceptance Criteria

  • Disasm test cases
  • Regression test cases
Author:TIHan
Assignees:-
Labels:

area-CodeGen-coreclr

Milestone:-

{
assert(emitHasLastIns() == (emitLastInsIG != nullptr));

return emitHasLastIns() && // there is an emitLastInstr

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.

Maybe this is for a separate PR, but I think we need to prevent peephole optimizations when we're in the prolog or epilog. I.e.,

 if (emitIGisInProlog(emitCurIG) || emitIGisInEpilog(emitCurIG))
{
return false;
}
#ifdef FEATURE_EH_FUNCLETS
if (emitIGisInFuncletProlog(emitCurIG) || emitIGisInFuncletEpilog(emitCurIG))
{
return false;
}
#endif

There is too much special handling in the prolog/epilog (e.g., unwinding) to allow peeps to kick in. There may be very specific cases where they are ok, but that requires some careful thinking.

@TIHan

Copy link
Copy Markdown
ContributorAuthor

/azp run runtime-coreclr superpmi-diffs

@azure-pipelines

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

@TIHan

Copy link
Copy Markdown
ContributorAuthor

/azp run runtime-coreclr superpmi-replay

@azure-pipelines

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

@TIHan

Copy link
Copy Markdown
ContributorAuthor

/azp run runtime-coreclr gcstress0x3-gcstress0xc

@TIHan

Copy link
Copy Markdown
ContributorAuthor

/azp run jitstress

@azure-pipelines

Copy link
Copy Markdown
No pipelines are associated with this pull request.

@azure-pipelines

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

@TIHan

Copy link
Copy Markdown
ContributorAuthor

/azp run runtime-coreclr jitstress

@azure-pipelines

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

@TIHan

TIHan commented Feb 24, 2023

Copy link
Copy Markdown
ContributorAuthor

@dotnet/jit-contrib @BruceForstall This is ready again. I ran gcstress and jistress, they passed CI. The current failures are unrelated.

@AndyAyersMS

Copy link
Copy Markdown
Member

Is there any way to avoid the TP impact in min opts?

@BruceForstall

Copy link
Copy Markdown
Contributor

Diffs

@BruceForstall

Copy link
Copy Markdown
Contributor

Is there any way to avoid the TP impact in min opts?

I believe it's a result of enabling backwards navigation in the insGroup/instrDesc (#80840), and maintaining those data structures. It's not obvious how this could only be done for non-MinOpts (and whether it would be advisable if it could be done).

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

LGTM

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 SuperPMI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Jit can generate pointless movs

6 participants

@TIHan@BruceForstall@tannergooding@AndyAyersMS@kunalspathak@markples
, '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

[JIT] X64 - Extend emitter peephole optimization of eliminating unnecessary mov instructions - #79381

Merged
TIHan merged 88 commits into
dotnet:mainfrom
TIHan:mov-elim-1
Feb 25, 2023
Merged

[JIT] X64 - Extend emitter peephole optimization of eliminating unnecessary mov instructions#79381
TIHan merged 88 commits into
dotnet:mainfrom
TIHan:mov-elim-1

Conversation

@TIHan

@TIHanTIHan commented Dec 8, 2022

Copy link
Copy Markdown
Contributor

Description

Resolves#10315 based on the example code given in the first post.

This will not eliminate all possible unnecessary mov instructions, but it handles more now.

The JIT's emitter already had an existing way of removing the instructions using:

boolemitter::AreUpper32BitsZero(regNumber reg)

But, it was only able to look back at one instruction.

This PR extends AreUpper32BitsZero to allow looking back up to 256 instructions(max limit of instructions for an IG).

Example diffs:

@@ -28,13 +28,9 @@ G_M17551_IG02: ; gcrefRegs=00000000 {}, byrefRegs=00000004 {rdx}, byref
movzx r8, byte ptr [rcx+01H]
movzx r9, byte ptr [rcx+02H]
movzx rcx, byte ptr [rcx+03H]
- mov eax, eax
movsx rax, byte ptr [rdx+rax]
- mov r8d, r8d
movsx r8, byte ptr [rdx+r8]
- mov r9d, r9d
movsx r9, byte ptr [rdx+r9]
- mov ecx, ecx
movsx rdx, byte ptr [rdx+rcx]
; byrRegs -[rdx]
shl eax, 18
@@ -43,12 +39,12 @@ G_M17551_IG02: ; gcrefRegs=00000000 {}, byrefRegs=00000004 {rdx}, byref
or eax, edx
or r8d, r9d
or eax, r8d
- ;; size=66 bbWeight=1 PerfScore 27.25+ ;; size=56 bbWeight=1 PerfScore 26.25
G_M17551_IG03: ; , epilog, nogc, extend
ret ;; size=1 bbWeight=1 PerfScore 1.00
-; Total bytes of code 67, prolog size 0, PerfScore 34.95, instruction count 19, allocated bytes for code 67 (MethodHash=ea74bb70) for method System.Buffers.Text.Base64:Decode(ulong,byref):int+; Total bytes of code 57, prolog size 0, PerfScore 32.95, instruction count 15, allocated bytes for code 57 (MethodHash=ea74bb70) for method System.Buffers.Text.Base64:Decode(ulong,byref):int

Diffs from the issue's example:
image

Acceptance Criteria

  • Disasm test case
  • Comments

@ghostghost added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Dec 8, 2022
@ghostghost assigned TIHanDec 8, 2022
@ghost

ghost commented Dec 8, 2022

Copy link
Copy Markdown

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

Issue Details

Description

Resolves #10315 based on the example code given in the first post.

This will not eliminate all possible unnecessary mov instructions, but it handles a little more than before in common cases.

The JIT's emitter already had an existing way of removing the instructions using:

boolemitter::AreUpper32BitsZero(regNumber reg)

But, it was only able to look back at one instruction.

This PR extends AreUpper32BitsZero to allow looking back at any number of instructions, but only if it's safe to do so. It does not keep a list of instructions, it only keeps a single unsigned int for tracking if a register has its upper 32-bits set to zero:

// IMPORTANT: Only contains information from **before** the last emitted instruction.// A lookup where each bit position corresponds to a register.// A bit that is set means the register's upper 32-bits are zero.// This effectively keeps track of which registers have their upper 32-bits set to zero.// GPRs (general-purpose registers) only.unsignedint upper32BitsZeroRegLookup;

Example diffs:

@@ -28,13 +28,9 @@ G_M17551_IG02: ; gcrefRegs=00000000 {}, byrefRegs=00000004 {rdx}, byref
movzx r8, byte ptr [rcx+01H]
movzx r9, byte ptr [rcx+02H]
movzx rcx, byte ptr [rcx+03H]
- mov eax, eax
movsx rax, byte ptr [rdx+rax]
- mov r8d, r8d
movsx r8, byte ptr [rdx+r8]
- mov r9d, r9d
movsx r9, byte ptr [rdx+r9]
- mov ecx, ecx
movsx rdx, byte ptr [rdx+rcx]
; byrRegs -[rdx]
shl eax, 18
@@ -43,12 +39,12 @@ G_M17551_IG02: ; gcrefRegs=00000000 {}, byrefRegs=00000004 {rdx}, byref
or eax, edx
or r8d, r9d
or eax, r8d
- ;; size=66 bbWeight=1 PerfScore 27.25+ ;; size=56 bbWeight=1 PerfScore 26.25
G_M17551_IG03: ; , epilog, nogc, extend
ret ;; size=1 bbWeight=1 PerfScore 1.00
-; Total bytes of code 67, prolog size 0, PerfScore 34.95, instruction count 19, allocated bytes for code 67 (MethodHash=ea74bb70) for method System.Buffers.Text.Base64:Decode(ulong,byref):int+; Total bytes of code 57, prolog size 0, PerfScore 32.95, instruction count 15, allocated bytes for code 57 (MethodHash=ea74bb70) for method System.Buffers.Text.Base64:Decode(ulong,byref):int

Diffs from the issue's example:
image

Diff Summary

Diffs are based on 1,398,805 contexts (351,415 MinOpts, 1,047,390 FullOpts).

MISSED contexts: base: 20, diff: 20

Overall (-1,124 bytes)
CollectionBase size (bytes)Diff size (bytes)
benchmarks.run.windows.x64.checked.mch25,002,887-124
coreclr_tests.run.windows.x64.checked.mch362,751,515-176
libraries.crossgen2.windows.x64.checked.mch35,246,384-153
libraries.pmi.windows.x64.checked.mch52,033,158-367
libraries_tests.pmi.windows.x64.checked.mch114,343,307-304
FullOpts (-1,124 bytes)
CollectionBase size (bytes)Diff size (bytes)
benchmarks.run.windows.x64.checked.mch23,285,431-124
coreclr_tests.run.windows.x64.checked.mch96,229,781-176
libraries.crossgen2.windows.x64.checked.mch35,245,195-153
libraries.pmi.windows.x64.checked.mch50,532,678-367
libraries_tests.pmi.windows.x64.checked.mch107,461,046-304
Details

Improvements/regressions per collection

CollectionContexts with diffsImprovementsRegressionsImprovements (bytes)Regressions (bytes)
benchmarks.run.windows.x64.checked.mch33300-124+0
coreclr_tests.run.windows.x64.checked.mch49490-176+0
libraries.crossgen2.windows.x64.checked.mch36360-153+0
libraries.pmi.windows.x64.checked.mch1181161-372+5
libraries_tests.pmi.windows.x64.checked.mch100990-304+0
3363301-1,129+5

Context information

CollectionDiffed contextsMinOptsFullOptsMissed, baseMissed, diff
benchmarks.run.windows.x64.checked.mch66,54516,37050,17500
coreclr_tests.run.windows.x64.checked.mch509,739320,923188,81600
libraries.crossgen2.windows.x64.checked.mch216,15415216,13944
libraries.pmi.windows.x64.checked.mch271,0974,958266,13988
libraries_tests.pmi.windows.x64.checked.mch335,2709,149326,12188
1,398,805351,4151,047,3902020

jit-analyze output

Acceptance Criteria

  • Disasm test cases
  • Regression test cases
Author:TIHan
Assignees:-
Labels:

area-CodeGen-coreclr

Milestone:-

{
assert(emitHasLastIns() == (emitLastInsIG != nullptr));

return emitHasLastIns() && // there is an emitLastInstr

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.

Maybe this is for a separate PR, but I think we need to prevent peephole optimizations when we're in the prolog or epilog. I.e.,

 if (emitIGisInProlog(emitCurIG) || emitIGisInEpilog(emitCurIG))
{
return false;
}
#ifdef FEATURE_EH_FUNCLETS
if (emitIGisInFuncletProlog(emitCurIG) || emitIGisInFuncletEpilog(emitCurIG))
{
return false;
}
#endif

There is too much special handling in the prolog/epilog (e.g., unwinding) to allow peeps to kick in. There may be very specific cases where they are ok, but that requires some careful thinking.

@TIHan

Copy link
Copy Markdown
ContributorAuthor

/azp run runtime-coreclr superpmi-diffs

@azure-pipelines

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

@TIHan

Copy link
Copy Markdown
ContributorAuthor

/azp run runtime-coreclr superpmi-replay

@azure-pipelines

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

@TIHan

Copy link
Copy Markdown
ContributorAuthor

/azp run runtime-coreclr gcstress0x3-gcstress0xc

@TIHan

Copy link
Copy Markdown
ContributorAuthor

/azp run jitstress

@azure-pipelines

Copy link
Copy Markdown
No pipelines are associated with this pull request.

@azure-pipelines

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

@TIHan

Copy link
Copy Markdown
ContributorAuthor

/azp run runtime-coreclr jitstress

@azure-pipelines

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

@TIHan

TIHan commented Feb 24, 2023

Copy link
Copy Markdown
ContributorAuthor

@dotnet/jit-contrib @BruceForstall This is ready again. I ran gcstress and jistress, they passed CI. The current failures are unrelated.

@AndyAyersMS

Copy link
Copy Markdown
Member

Is there any way to avoid the TP impact in min opts?

@BruceForstall

Copy link
Copy Markdown
Contributor

Diffs

@BruceForstall

Copy link
Copy Markdown
Contributor

Is there any way to avoid the TP impact in min opts?

I believe it's a result of enabling backwards navigation in the insGroup/instrDesc (#80840), and maintaining those data structures. It's not obvious how this could only be done for non-MinOpts (and whether it would be advisable if it could be done).

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

LGTM

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 SuperPMI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Jit can generate pointless movs

6 participants

@TIHan@BruceForstall@tannergooding@AndyAyersMS@kunalspathak@markples
, '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

[JIT] X64 - Extend emitter peephole optimization of eliminating unnecessary mov instructions - #79381

Merged
TIHan merged 88 commits into
dotnet:mainfrom
TIHan:mov-elim-1
Feb 25, 2023
Merged

[JIT] X64 - Extend emitter peephole optimization of eliminating unnecessary mov instructions#79381
TIHan merged 88 commits into
dotnet:mainfrom
TIHan:mov-elim-1

Conversation

@TIHan

@TIHanTIHan commented Dec 8, 2022

Copy link
Copy Markdown
Contributor

Description

Resolves#10315 based on the example code given in the first post.

This will not eliminate all possible unnecessary mov instructions, but it handles more now.

The JIT's emitter already had an existing way of removing the instructions using:

boolemitter::AreUpper32BitsZero(regNumber reg)

But, it was only able to look back at one instruction.

This PR extends AreUpper32BitsZero to allow looking back up to 256 instructions(max limit of instructions for an IG).

Example diffs:

@@ -28,13 +28,9 @@ G_M17551_IG02: ; gcrefRegs=00000000 {}, byrefRegs=00000004 {rdx}, byref
movzx r8, byte ptr [rcx+01H]
movzx r9, byte ptr [rcx+02H]
movzx rcx, byte ptr [rcx+03H]
- mov eax, eax
movsx rax, byte ptr [rdx+rax]
- mov r8d, r8d
movsx r8, byte ptr [rdx+r8]
- mov r9d, r9d
movsx r9, byte ptr [rdx+r9]
- mov ecx, ecx
movsx rdx, byte ptr [rdx+rcx]
; byrRegs -[rdx]
shl eax, 18
@@ -43,12 +39,12 @@ G_M17551_IG02: ; gcrefRegs=00000000 {}, byrefRegs=00000004 {rdx}, byref
or eax, edx
or r8d, r9d
or eax, r8d
- ;; size=66 bbWeight=1 PerfScore 27.25+ ;; size=56 bbWeight=1 PerfScore 26.25
G_M17551_IG03: ; , epilog, nogc, extend
ret ;; size=1 bbWeight=1 PerfScore 1.00
-; Total bytes of code 67, prolog size 0, PerfScore 34.95, instruction count 19, allocated bytes for code 67 (MethodHash=ea74bb70) for method System.Buffers.Text.Base64:Decode(ulong,byref):int+; Total bytes of code 57, prolog size 0, PerfScore 32.95, instruction count 15, allocated bytes for code 57 (MethodHash=ea74bb70) for method System.Buffers.Text.Base64:Decode(ulong,byref):int

Diffs from the issue's example:
image

Acceptance Criteria

  • Disasm test case
  • Comments

@ghostghost added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Dec 8, 2022
@ghostghost assigned TIHanDec 8, 2022
@ghost

ghost commented Dec 8, 2022

Copy link
Copy Markdown

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

Issue Details

Description

Resolves #10315 based on the example code given in the first post.

This will not eliminate all possible unnecessary mov instructions, but it handles a little more than before in common cases.

The JIT's emitter already had an existing way of removing the instructions using:

boolemitter::AreUpper32BitsZero(regNumber reg)

But, it was only able to look back at one instruction.

This PR extends AreUpper32BitsZero to allow looking back at any number of instructions, but only if it's safe to do so. It does not keep a list of instructions, it only keeps a single unsigned int for tracking if a register has its upper 32-bits set to zero:

// IMPORTANT: Only contains information from **before** the last emitted instruction.// A lookup where each bit position corresponds to a register.// A bit that is set means the register's upper 32-bits are zero.// This effectively keeps track of which registers have their upper 32-bits set to zero.// GPRs (general-purpose registers) only.unsignedint upper32BitsZeroRegLookup;

Example diffs:

@@ -28,13 +28,9 @@ G_M17551_IG02: ; gcrefRegs=00000000 {}, byrefRegs=00000004 {rdx}, byref
movzx r8, byte ptr [rcx+01H]
movzx r9, byte ptr [rcx+02H]
movzx rcx, byte ptr [rcx+03H]
- mov eax, eax
movsx rax, byte ptr [rdx+rax]
- mov r8d, r8d
movsx r8, byte ptr [rdx+r8]
- mov r9d, r9d
movsx r9, byte ptr [rdx+r9]
- mov ecx, ecx
movsx rdx, byte ptr [rdx+rcx]
; byrRegs -[rdx]
shl eax, 18
@@ -43,12 +39,12 @@ G_M17551_IG02: ; gcrefRegs=00000000 {}, byrefRegs=00000004 {rdx}, byref
or eax, edx
or r8d, r9d
or eax, r8d
- ;; size=66 bbWeight=1 PerfScore 27.25+ ;; size=56 bbWeight=1 PerfScore 26.25
G_M17551_IG03: ; , epilog, nogc, extend
ret ;; size=1 bbWeight=1 PerfScore 1.00
-; Total bytes of code 67, prolog size 0, PerfScore 34.95, instruction count 19, allocated bytes for code 67 (MethodHash=ea74bb70) for method System.Buffers.Text.Base64:Decode(ulong,byref):int+; Total bytes of code 57, prolog size 0, PerfScore 32.95, instruction count 15, allocated bytes for code 57 (MethodHash=ea74bb70) for method System.Buffers.Text.Base64:Decode(ulong,byref):int

Diffs from the issue's example:
image

Diff Summary

Diffs are based on 1,398,805 contexts (351,415 MinOpts, 1,047,390 FullOpts).

MISSED contexts: base: 20, diff: 20

Overall (-1,124 bytes)
CollectionBase size (bytes)Diff size (bytes)
benchmarks.run.windows.x64.checked.mch25,002,887-124
coreclr_tests.run.windows.x64.checked.mch362,751,515-176
libraries.crossgen2.windows.x64.checked.mch35,246,384-153
libraries.pmi.windows.x64.checked.mch52,033,158-367
libraries_tests.pmi.windows.x64.checked.mch114,343,307-304
FullOpts (-1,124 bytes)
CollectionBase size (bytes)Diff size (bytes)
benchmarks.run.windows.x64.checked.mch23,285,431-124
coreclr_tests.run.windows.x64.checked.mch96,229,781-176
libraries.crossgen2.windows.x64.checked.mch35,245,195-153
libraries.pmi.windows.x64.checked.mch50,532,678-367
libraries_tests.pmi.windows.x64.checked.mch107,461,046-304
Details

Improvements/regressions per collection

CollectionContexts with diffsImprovementsRegressionsImprovements (bytes)Regressions (bytes)
benchmarks.run.windows.x64.checked.mch33300-124+0
coreclr_tests.run.windows.x64.checked.mch49490-176+0
libraries.crossgen2.windows.x64.checked.mch36360-153+0
libraries.pmi.windows.x64.checked.mch1181161-372+5
libraries_tests.pmi.windows.x64.checked.mch100990-304+0
3363301-1,129+5

Context information

CollectionDiffed contextsMinOptsFullOptsMissed, baseMissed, diff
benchmarks.run.windows.x64.checked.mch66,54516,37050,17500
coreclr_tests.run.windows.x64.checked.mch509,739320,923188,81600
libraries.crossgen2.windows.x64.checked.mch216,15415216,13944
libraries.pmi.windows.x64.checked.mch271,0974,958266,13988
libraries_tests.pmi.windows.x64.checked.mch335,2709,149326,12188
1,398,805351,4151,047,3902020

jit-analyze output

Acceptance Criteria

  • Disasm test cases
  • Regression test cases
Author:TIHan
Assignees:-
Labels:

area-CodeGen-coreclr

Milestone:-

{
assert(emitHasLastIns() == (emitLastInsIG != nullptr));

return emitHasLastIns() && // there is an emitLastInstr

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.

Maybe this is for a separate PR, but I think we need to prevent peephole optimizations when we're in the prolog or epilog. I.e.,

 if (emitIGisInProlog(emitCurIG) || emitIGisInEpilog(emitCurIG))
{
return false;
}
#ifdef FEATURE_EH_FUNCLETS
if (emitIGisInFuncletProlog(emitCurIG) || emitIGisInFuncletEpilog(emitCurIG))
{
return false;
}
#endif

There is too much special handling in the prolog/epilog (e.g., unwinding) to allow peeps to kick in. There may be very specific cases where they are ok, but that requires some careful thinking.

@TIHan

Copy link
Copy Markdown
ContributorAuthor

/azp run runtime-coreclr superpmi-diffs

@azure-pipelines

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

@TIHan

Copy link
Copy Markdown
ContributorAuthor

/azp run runtime-coreclr superpmi-replay

@azure-pipelines

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

@TIHan

Copy link
Copy Markdown
ContributorAuthor

/azp run runtime-coreclr gcstress0x3-gcstress0xc

@TIHan

Copy link
Copy Markdown
ContributorAuthor

/azp run jitstress

@azure-pipelines

Copy link
Copy Markdown
No pipelines are associated with this pull request.

@azure-pipelines

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

@TIHan

Copy link
Copy Markdown
ContributorAuthor

/azp run runtime-coreclr jitstress

@azure-pipelines

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

@TIHan

TIHan commented Feb 24, 2023

Copy link
Copy Markdown
ContributorAuthor

@dotnet/jit-contrib @BruceForstall This is ready again. I ran gcstress and jistress, they passed CI. The current failures are unrelated.

@AndyAyersMS

Copy link
Copy Markdown
Member

Is there any way to avoid the TP impact in min opts?

@BruceForstall

Copy link
Copy Markdown
Contributor

Diffs

@BruceForstall

Copy link
Copy Markdown
Contributor

Is there any way to avoid the TP impact in min opts?

I believe it's a result of enabling backwards navigation in the insGroup/instrDesc (#80840), and maintaining those data structures. It's not obvious how this could only be done for non-MinOpts (and whether it would be advisable if it could be done).

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

LGTM

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 SuperPMI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Jit can generate pointless movs

6 participants

@TIHan@BruceForstall@tannergooding@AndyAyersMS@kunalspathak@markples
, '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

[JIT] X64 - Extend emitter peephole optimization of eliminating unnecessary mov instructions - #79381

Merged
TIHan merged 88 commits into
dotnet:mainfrom
TIHan:mov-elim-1
Feb 25, 2023
Merged

[JIT] X64 - Extend emitter peephole optimization of eliminating unnecessary mov instructions#79381
TIHan merged 88 commits into
dotnet:mainfrom
TIHan:mov-elim-1

Conversation

@TIHan

@TIHanTIHan commented Dec 8, 2022

Copy link
Copy Markdown
Contributor

Description

Resolves#10315 based on the example code given in the first post.

This will not eliminate all possible unnecessary mov instructions, but it handles more now.

The JIT's emitter already had an existing way of removing the instructions using:

boolemitter::AreUpper32BitsZero(regNumber reg)

But, it was only able to look back at one instruction.

This PR extends AreUpper32BitsZero to allow looking back up to 256 instructions(max limit of instructions for an IG).

Example diffs:

@@ -28,13 +28,9 @@ G_M17551_IG02: ; gcrefRegs=00000000 {}, byrefRegs=00000004 {rdx}, byref
movzx r8, byte ptr [rcx+01H]
movzx r9, byte ptr [rcx+02H]
movzx rcx, byte ptr [rcx+03H]
- mov eax, eax
movsx rax, byte ptr [rdx+rax]
- mov r8d, r8d
movsx r8, byte ptr [rdx+r8]
- mov r9d, r9d
movsx r9, byte ptr [rdx+r9]
- mov ecx, ecx
movsx rdx, byte ptr [rdx+rcx]
; byrRegs -[rdx]
shl eax, 18
@@ -43,12 +39,12 @@ G_M17551_IG02: ; gcrefRegs=00000000 {}, byrefRegs=00000004 {rdx}, byref
or eax, edx
or r8d, r9d
or eax, r8d
- ;; size=66 bbWeight=1 PerfScore 27.25+ ;; size=56 bbWeight=1 PerfScore 26.25
G_M17551_IG03: ; , epilog, nogc, extend
ret ;; size=1 bbWeight=1 PerfScore 1.00
-; Total bytes of code 67, prolog size 0, PerfScore 34.95, instruction count 19, allocated bytes for code 67 (MethodHash=ea74bb70) for method System.Buffers.Text.Base64:Decode(ulong,byref):int+; Total bytes of code 57, prolog size 0, PerfScore 32.95, instruction count 15, allocated bytes for code 57 (MethodHash=ea74bb70) for method System.Buffers.Text.Base64:Decode(ulong,byref):int

Diffs from the issue's example:
image

Acceptance Criteria

  • Disasm test case
  • Comments

@ghostghost added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Dec 8, 2022
@ghostghost assigned TIHanDec 8, 2022
@ghost

ghost commented Dec 8, 2022

Copy link
Copy Markdown

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

Issue Details

Description

Resolves #10315 based on the example code given in the first post.

This will not eliminate all possible unnecessary mov instructions, but it handles a little more than before in common cases.

The JIT's emitter already had an existing way of removing the instructions using:

boolemitter::AreUpper32BitsZero(regNumber reg)

But, it was only able to look back at one instruction.

This PR extends AreUpper32BitsZero to allow looking back at any number of instructions, but only if it's safe to do so. It does not keep a list of instructions, it only keeps a single unsigned int for tracking if a register has its upper 32-bits set to zero:

// IMPORTANT: Only contains information from **before** the last emitted instruction.// A lookup where each bit position corresponds to a register.// A bit that is set means the register's upper 32-bits are zero.// This effectively keeps track of which registers have their upper 32-bits set to zero.// GPRs (general-purpose registers) only.unsignedint upper32BitsZeroRegLookup;

Example diffs:

@@ -28,13 +28,9 @@ G_M17551_IG02: ; gcrefRegs=00000000 {}, byrefRegs=00000004 {rdx}, byref
movzx r8, byte ptr [rcx+01H]
movzx r9, byte ptr [rcx+02H]
movzx rcx, byte ptr [rcx+03H]
- mov eax, eax
movsx rax, byte ptr [rdx+rax]
- mov r8d, r8d
movsx r8, byte ptr [rdx+r8]
- mov r9d, r9d
movsx r9, byte ptr [rdx+r9]
- mov ecx, ecx
movsx rdx, byte ptr [rdx+rcx]
; byrRegs -[rdx]
shl eax, 18
@@ -43,12 +39,12 @@ G_M17551_IG02: ; gcrefRegs=00000000 {}, byrefRegs=00000004 {rdx}, byref
or eax, edx
or r8d, r9d
or eax, r8d
- ;; size=66 bbWeight=1 PerfScore 27.25+ ;; size=56 bbWeight=1 PerfScore 26.25
G_M17551_IG03: ; , epilog, nogc, extend
ret ;; size=1 bbWeight=1 PerfScore 1.00
-; Total bytes of code 67, prolog size 0, PerfScore 34.95, instruction count 19, allocated bytes for code 67 (MethodHash=ea74bb70) for method System.Buffers.Text.Base64:Decode(ulong,byref):int+; Total bytes of code 57, prolog size 0, PerfScore 32.95, instruction count 15, allocated bytes for code 57 (MethodHash=ea74bb70) for method System.Buffers.Text.Base64:Decode(ulong,byref):int

Diffs from the issue's example:
image

Diff Summary

Diffs are based on 1,398,805 contexts (351,415 MinOpts, 1,047,390 FullOpts).

MISSED contexts: base: 20, diff: 20

Overall (-1,124 bytes)
CollectionBase size (bytes)Diff size (bytes)
benchmarks.run.windows.x64.checked.mch25,002,887-124
coreclr_tests.run.windows.x64.checked.mch362,751,515-176
libraries.crossgen2.windows.x64.checked.mch35,246,384-153
libraries.pmi.windows.x64.checked.mch52,033,158-367
libraries_tests.pmi.windows.x64.checked.mch114,343,307-304
FullOpts (-1,124 bytes)
CollectionBase size (bytes)Diff size (bytes)
benchmarks.run.windows.x64.checked.mch23,285,431-124
coreclr_tests.run.windows.x64.checked.mch96,229,781-176
libraries.crossgen2.windows.x64.checked.mch35,245,195-153
libraries.pmi.windows.x64.checked.mch50,532,678-367
libraries_tests.pmi.windows.x64.checked.mch107,461,046-304
Details

Improvements/regressions per collection

CollectionContexts with diffsImprovementsRegressionsImprovements (bytes)Regressions (bytes)
benchmarks.run.windows.x64.checked.mch33300-124+0
coreclr_tests.run.windows.x64.checked.mch49490-176+0
libraries.crossgen2.windows.x64.checked.mch36360-153+0
libraries.pmi.windows.x64.checked.mch1181161-372+5
libraries_tests.pmi.windows.x64.checked.mch100990-304+0
3363301-1,129+5

Context information

CollectionDiffed contextsMinOptsFullOptsMissed, baseMissed, diff
benchmarks.run.windows.x64.checked.mch66,54516,37050,17500
coreclr_tests.run.windows.x64.checked.mch509,739320,923188,81600
libraries.crossgen2.windows.x64.checked.mch216,15415216,13944
libraries.pmi.windows.x64.checked.mch271,0974,958266,13988
libraries_tests.pmi.windows.x64.checked.mch335,2709,149326,12188
1,398,805351,4151,047,3902020

jit-analyze output

Acceptance Criteria

  • Disasm test cases
  • Regression test cases
Author:TIHan
Assignees:-
Labels:

area-CodeGen-coreclr

Milestone:-

{
assert(emitHasLastIns() == (emitLastInsIG != nullptr));

return emitHasLastIns() && // there is an emitLastInstr

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.

Maybe this is for a separate PR, but I think we need to prevent peephole optimizations when we're in the prolog or epilog. I.e.,

 if (emitIGisInProlog(emitCurIG) || emitIGisInEpilog(emitCurIG))
{
return false;
}
#ifdef FEATURE_EH_FUNCLETS
if (emitIGisInFuncletProlog(emitCurIG) || emitIGisInFuncletEpilog(emitCurIG))
{
return false;
}
#endif

There is too much special handling in the prolog/epilog (e.g., unwinding) to allow peeps to kick in. There may be very specific cases where they are ok, but that requires some careful thinking.

@TIHan

Copy link
Copy Markdown
ContributorAuthor

/azp run runtime-coreclr superpmi-diffs

@azure-pipelines

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

@TIHan

Copy link
Copy Markdown
ContributorAuthor

/azp run runtime-coreclr superpmi-replay

@azure-pipelines

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

@TIHan

Copy link
Copy Markdown
ContributorAuthor

/azp run runtime-coreclr gcstress0x3-gcstress0xc

@TIHan

Copy link
Copy Markdown
ContributorAuthor

/azp run jitstress

@azure-pipelines

Copy link
Copy Markdown
No pipelines are associated with this pull request.

@azure-pipelines

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

@TIHan

Copy link
Copy Markdown
ContributorAuthor

/azp run runtime-coreclr jitstress

@azure-pipelines

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

@TIHan

TIHan commented Feb 24, 2023

Copy link
Copy Markdown
ContributorAuthor

@dotnet/jit-contrib @BruceForstall This is ready again. I ran gcstress and jistress, they passed CI. The current failures are unrelated.

@AndyAyersMS

Copy link
Copy Markdown
Member

Is there any way to avoid the TP impact in min opts?

@BruceForstall

Copy link
Copy Markdown
Contributor

Diffs

@BruceForstall

Copy link
Copy Markdown
Contributor

Is there any way to avoid the TP impact in min opts?

I believe it's a result of enabling backwards navigation in the insGroup/instrDesc (#80840), and maintaining those data structures. It's not obvious how this could only be done for non-MinOpts (and whether it would be advisable if it could be done).

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

LGTM

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 SuperPMI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Jit can generate pointless movs

6 participants

@TIHan@BruceForstall@tannergooding@AndyAyersMS@kunalspathak@markples
, '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

[JIT] X64 - Extend emitter peephole optimization of eliminating unnecessary mov instructions - #79381

Merged
TIHan merged 88 commits into
dotnet:mainfrom
TIHan:mov-elim-1
Feb 25, 2023
Merged

[JIT] X64 - Extend emitter peephole optimization of eliminating unnecessary mov instructions#79381
TIHan merged 88 commits into
dotnet:mainfrom
TIHan:mov-elim-1

Conversation

@TIHan

@TIHanTIHan commented Dec 8, 2022

Copy link
Copy Markdown
Contributor

Description

Resolves#10315 based on the example code given in the first post.

This will not eliminate all possible unnecessary mov instructions, but it handles more now.

The JIT's emitter already had an existing way of removing the instructions using:

boolemitter::AreUpper32BitsZero(regNumber reg)

But, it was only able to look back at one instruction.

This PR extends AreUpper32BitsZero to allow looking back up to 256 instructions(max limit of instructions for an IG).

Example diffs:

@@ -28,13 +28,9 @@ G_M17551_IG02: ; gcrefRegs=00000000 {}, byrefRegs=00000004 {rdx}, byref
movzx r8, byte ptr [rcx+01H]
movzx r9, byte ptr [rcx+02H]
movzx rcx, byte ptr [rcx+03H]
- mov eax, eax
movsx rax, byte ptr [rdx+rax]
- mov r8d, r8d
movsx r8, byte ptr [rdx+r8]
- mov r9d, r9d
movsx r9, byte ptr [rdx+r9]
- mov ecx, ecx
movsx rdx, byte ptr [rdx+rcx]
; byrRegs -[rdx]
shl eax, 18
@@ -43,12 +39,12 @@ G_M17551_IG02: ; gcrefRegs=00000000 {}, byrefRegs=00000004 {rdx}, byref
or eax, edx
or r8d, r9d
or eax, r8d
- ;; size=66 bbWeight=1 PerfScore 27.25+ ;; size=56 bbWeight=1 PerfScore 26.25
G_M17551_IG03: ; , epilog, nogc, extend
ret ;; size=1 bbWeight=1 PerfScore 1.00
-; Total bytes of code 67, prolog size 0, PerfScore 34.95, instruction count 19, allocated bytes for code 67 (MethodHash=ea74bb70) for method System.Buffers.Text.Base64:Decode(ulong,byref):int+; Total bytes of code 57, prolog size 0, PerfScore 32.95, instruction count 15, allocated bytes for code 57 (MethodHash=ea74bb70) for method System.Buffers.Text.Base64:Decode(ulong,byref):int

Diffs from the issue's example:
image

Acceptance Criteria

  • Disasm test case
  • Comments

@ghostghost added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Dec 8, 2022
@ghostghost assigned TIHanDec 8, 2022
@ghost

ghost commented Dec 8, 2022

Copy link
Copy Markdown

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

Issue Details

Description

Resolves #10315 based on the example code given in the first post.

This will not eliminate all possible unnecessary mov instructions, but it handles a little more than before in common cases.

The JIT's emitter already had an existing way of removing the instructions using:

boolemitter::AreUpper32BitsZero(regNumber reg)

But, it was only able to look back at one instruction.

This PR extends AreUpper32BitsZero to allow looking back at any number of instructions, but only if it's safe to do so. It does not keep a list of instructions, it only keeps a single unsigned int for tracking if a register has its upper 32-bits set to zero:

// IMPORTANT: Only contains information from **before** the last emitted instruction.// A lookup where each bit position corresponds to a register.// A bit that is set means the register's upper 32-bits are zero.// This effectively keeps track of which registers have their upper 32-bits set to zero.// GPRs (general-purpose registers) only.unsignedint upper32BitsZeroRegLookup;

Example diffs:

@@ -28,13 +28,9 @@ G_M17551_IG02: ; gcrefRegs=00000000 {}, byrefRegs=00000004 {rdx}, byref
movzx r8, byte ptr [rcx+01H]
movzx r9, byte ptr [rcx+02H]
movzx rcx, byte ptr [rcx+03H]
- mov eax, eax
movsx rax, byte ptr [rdx+rax]
- mov r8d, r8d
movsx r8, byte ptr [rdx+r8]
- mov r9d, r9d
movsx r9, byte ptr [rdx+r9]
- mov ecx, ecx
movsx rdx, byte ptr [rdx+rcx]
; byrRegs -[rdx]
shl eax, 18
@@ -43,12 +39,12 @@ G_M17551_IG02: ; gcrefRegs=00000000 {}, byrefRegs=00000004 {rdx}, byref
or eax, edx
or r8d, r9d
or eax, r8d
- ;; size=66 bbWeight=1 PerfScore 27.25+ ;; size=56 bbWeight=1 PerfScore 26.25
G_M17551_IG03: ; , epilog, nogc, extend
ret ;; size=1 bbWeight=1 PerfScore 1.00
-; Total bytes of code 67, prolog size 0, PerfScore 34.95, instruction count 19, allocated bytes for code 67 (MethodHash=ea74bb70) for method System.Buffers.Text.Base64:Decode(ulong,byref):int+; Total bytes of code 57, prolog size 0, PerfScore 32.95, instruction count 15, allocated bytes for code 57 (MethodHash=ea74bb70) for method System.Buffers.Text.Base64:Decode(ulong,byref):int

Diffs from the issue's example:
image

Diff Summary

Diffs are based on 1,398,805 contexts (351,415 MinOpts, 1,047,390 FullOpts).

MISSED contexts: base: 20, diff: 20

Overall (-1,124 bytes)
CollectionBase size (bytes)Diff size (bytes)
benchmarks.run.windows.x64.checked.mch25,002,887-124
coreclr_tests.run.windows.x64.checked.mch362,751,515-176
libraries.crossgen2.windows.x64.checked.mch35,246,384-153
libraries.pmi.windows.x64.checked.mch52,033,158-367
libraries_tests.pmi.windows.x64.checked.mch114,343,307-304
FullOpts (-1,124 bytes)
CollectionBase size (bytes)Diff size (bytes)
benchmarks.run.windows.x64.checked.mch23,285,431-124
coreclr_tests.run.windows.x64.checked.mch96,229,781-176
libraries.crossgen2.windows.x64.checked.mch35,245,195-153
libraries.pmi.windows.x64.checked.mch50,532,678-367
libraries_tests.pmi.windows.x64.checked.mch107,461,046-304
Details

Improvements/regressions per collection

CollectionContexts with diffsImprovementsRegressionsImprovements (bytes)Regressions (bytes)
benchmarks.run.windows.x64.checked.mch33300-124+0
coreclr_tests.run.windows.x64.checked.mch49490-176+0
libraries.crossgen2.windows.x64.checked.mch36360-153+0
libraries.pmi.windows.x64.checked.mch1181161-372+5
libraries_tests.pmi.windows.x64.checked.mch100990-304+0
3363301-1,129+5

Context information

CollectionDiffed contextsMinOptsFullOptsMissed, baseMissed, diff
benchmarks.run.windows.x64.checked.mch66,54516,37050,17500
coreclr_tests.run.windows.x64.checked.mch509,739320,923188,81600
libraries.crossgen2.windows.x64.checked.mch216,15415216,13944
libraries.pmi.windows.x64.checked.mch271,0974,958266,13988
libraries_tests.pmi.windows.x64.checked.mch335,2709,149326,12188
1,398,805351,4151,047,3902020

jit-analyze output

Acceptance Criteria

  • Disasm test cases
  • Regression test cases
Author:TIHan
Assignees:-
Labels:

area-CodeGen-coreclr

Milestone:-

{
assert(emitHasLastIns() == (emitLastInsIG != nullptr));

return emitHasLastIns() && // there is an emitLastInstr

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.

Maybe this is for a separate PR, but I think we need to prevent peephole optimizations when we're in the prolog or epilog. I.e.,

 if (emitIGisInProlog(emitCurIG) || emitIGisInEpilog(emitCurIG))
{
return false;
}
#ifdef FEATURE_EH_FUNCLETS
if (emitIGisInFuncletProlog(emitCurIG) || emitIGisInFuncletEpilog(emitCurIG))
{
return false;
}
#endif

There is too much special handling in the prolog/epilog (e.g., unwinding) to allow peeps to kick in. There may be very specific cases where they are ok, but that requires some careful thinking.

@TIHan

Copy link
Copy Markdown
ContributorAuthor

/azp run runtime-coreclr superpmi-diffs

@azure-pipelines

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

@TIHan

Copy link
Copy Markdown
ContributorAuthor

/azp run runtime-coreclr superpmi-replay

@azure-pipelines

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

@TIHan

Copy link
Copy Markdown
ContributorAuthor

/azp run runtime-coreclr gcstress0x3-gcstress0xc

@TIHan

Copy link
Copy Markdown
ContributorAuthor

/azp run jitstress

@azure-pipelines

Copy link
Copy Markdown
No pipelines are associated with this pull request.

@azure-pipelines

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

@TIHan

Copy link
Copy Markdown
ContributorAuthor

/azp run runtime-coreclr jitstress

@azure-pipelines

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

@TIHan

TIHan commented Feb 24, 2023

Copy link
Copy Markdown
ContributorAuthor

@dotnet/jit-contrib @BruceForstall This is ready again. I ran gcstress and jistress, they passed CI. The current failures are unrelated.

@AndyAyersMS

Copy link
Copy Markdown
Member

Is there any way to avoid the TP impact in min opts?

@BruceForstall

Copy link
Copy Markdown
Contributor

Diffs

@BruceForstall

Copy link
Copy Markdown
Contributor

Is there any way to avoid the TP impact in min opts?

I believe it's a result of enabling backwards navigation in the insGroup/instrDesc (#80840), and maintaining those data structures. It's not obvious how this could only be done for non-MinOpts (and whether it would be advisable if it could be done).

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

LGTM

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 SuperPMI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Jit can generate pointless movs

6 participants

@TIHan@BruceForstall@tannergooding@AndyAyersMS@kunalspathak@markples