Skip to content

JIT: Fix constrained call dereferences happening too early - #86638

Merged
jakobbotsch merged 3 commits into
dotnet:mainfrom
jakobbotsch:fix-73615
Apr 16, 2024
Merged

JIT: Fix constrained call dereferences happening too early#86638
jakobbotsch merged 3 commits into
dotnet:mainfrom
jakobbotsch:fix-73615

Conversation

@jakobbotsch

Copy link
Copy Markdown
Member

Constrained calls in IL include an implicit dereference that occurs as part of the call. The JIT was adding this dereference on top of the 'this' argument tree, which makes it happen too early (before other arguments are evaluated). This changes the importer to spill when necessary to preserve ordering.

For:

 .method private hidebysig static void Foo(class Runtime_73615/C arg) cil managed noinlining
{
// Code size 21 (0x15)
.maxstack 2
IL_0000: ldarga.s arg
IL_0002: ldarga.s arg
IL_0004: call int32 Runtime_73615::Bar(class Runtime_73615/C&)
IL_0009: constrained. Runtime_73615/C
IL_000f: callvirt instance void Runtime_73615/C::Baz(int32)
IL_0014: ret
}

Before:

*****BB01STMT00000 ( 0x000[E-] ... 0x014 )
[000003] --C-G------CALL nullcheck void Runtime_73615+C:Baz(int):this
[000004] n---G------this├──▌IND ref
[000000] -----------└──▌LCL_ADDR long V00 arg0 [+0]
[000002] --C-G------ arg1 └──▌CALL int Runtime_73615:Bar(byref):int
[000001] ----------- arg0 └──▌LCL_ADDR byref V00 arg0 [+0]

After:

*****BB01STMT00000 ( 0x000[E-] ... 0x014 )
[000005] -AC-G------ASG int
[000004] D------N---├──▌LCL_VAR int V02 tmp1
[000002] --C-G------└──▌CALL int Runtime_73615:Bar(byref):int
[000001] ----------- arg0 └──▌LCL_ADDR byref V00 arg0 [+0]
*****BB01STMT00001 ( ??? ... ??? )
[000003] --C-G------CALL nullcheck void Runtime_73615+C:Baz(int):this
[000007] n---G------this├──▌IND ref
[000000] -----------└──▌LCL_ADDR long V00 arg0 [+0]
[000006] ----------- arg1 └──▌LCL_VAR int V02 tmp1

Fix#73615

Some regressions/improvements are expected due to more spilling in these cases.

Constrained calls in IL include an implicit dereference that occurs as
part of the call. The JIT was adding this dereference on top of the
'this' argument tree, which makes it happen too early (before other
arguments are evaluated). This changes the importer to spill when
necessary to preserve ordering.
For:
```cil
.method private hidebysig static void Foo(class Runtime_73615/C arg) cil managed noinlining
{
// Code size 21 (0x15)
.maxstack 2
IL_0000: ldarga.s arg
IL_0002: ldarga.s arg
IL_0004: call int32 Runtime_73615::Bar(class Runtime_73615/C&)
IL_0009: constrained. Runtime_73615/C
IL_000f: callvirt instance void Runtime_73615/C::Baz(int32)
IL_0014: ret
}
```
Before:
```
***** BB01
STMT00000 ( 0x000[E-] ... 0x014 )
[000003] --C-G------ ▌ CALL nullcheck void Runtime_73615+C:Baz(int):this
[000004] n---G------ this ├──▌ IND ref
[000000] ----------- │ └──▌ LCL_ADDR long V00 arg0 [+0]
[000002] --C-G------ arg1 └──▌ CALL int Runtime_73615:Bar(byref):int
[000001] ----------- arg0 └──▌ LCL_ADDR byref V00 arg0 [+0]
```
After:
```
***** BB01
STMT00000 ( 0x000[E-] ... 0x014 )
[000005] -AC-G------ ▌ ASG int
[000004] D------N--- ├──▌ LCL_VAR int V02 tmp1
[000002] --C-G------ └──▌ CALL int Runtime_73615:Bar(byref):int
[000001] ----------- arg0 └──▌ LCL_ADDR byref V00 arg0 [+0]
***** BB01
STMT00001 ( ??? ... ??? )
[000003] --C-G------ ▌ CALL nullcheck void Runtime_73615+C:Baz(int):this
[000007] n---G------ this ├──▌ IND ref
[000000] ----------- │ └──▌ LCL_ADDR long V00 arg0 [+0]
[000006] ----------- arg1 └──▌ LCL_VAR int V02 tmp1
```
Fixdotnet#73615
@ghostghost added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label May 23, 2023
@ghost

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

Constrained calls in IL include an implicit dereference that occurs as part of the call. The JIT was adding this dereference on top of the 'this' argument tree, which makes it happen too early (before other arguments are evaluated). This changes the importer to spill when necessary to preserve ordering.

For:

 .method private hidebysig static void Foo(class Runtime_73615/C arg) cil managed noinlining
{
// Code size 21 (0x15)
.maxstack 2
IL_0000: ldarga.s arg
IL_0002: ldarga.s arg
IL_0004: call int32 Runtime_73615::Bar(class Runtime_73615/C&)
IL_0009: constrained. Runtime_73615/C
IL_000f: callvirt instance void Runtime_73615/C::Baz(int32)
IL_0014: ret
}

Before:

*****BB01STMT00000 ( 0x000[E-] ... 0x014 )
[000003] --C-G------CALL nullcheck void Runtime_73615+C:Baz(int):this
[000004] n---G------this├──▌IND ref
[000000] -----------└──▌LCL_ADDR long V00 arg0 [+0]
[000002] --C-G------ arg1 └──▌CALL int Runtime_73615:Bar(byref):int
[000001] ----------- arg0 └──▌LCL_ADDR byref V00 arg0 [+0]

After:

*****BB01STMT00000 ( 0x000[E-] ... 0x014 )
[000005] -AC-G------ASG int
[000004] D------N---├──▌LCL_VAR int V02 tmp1
[000002] --C-G------└──▌CALL int Runtime_73615:Bar(byref):int
[000001] ----------- arg0 └──▌LCL_ADDR byref V00 arg0 [+0]
*****BB01STMT00001 ( ??? ... ??? )
[000003] --C-G------CALL nullcheck void Runtime_73615+C:Baz(int):this
[000007] n---G------this├──▌IND ref
[000000] -----------└──▌LCL_ADDR long V00 arg0 [+0]
[000006] ----------- arg1 └──▌LCL_VAR int V02 tmp1

Fix #73615

Some regressions/improvements are expected due to more spilling in these cases.

Author:jakobbotsch
Assignees:-
Labels:

area-CodeGen-coreclr

Milestone:-

@jakobbotsch

Copy link
Copy Markdown
MemberAuthor

Diffs.

cc @dotnet/jit-contrib PTAL @markples

@jakobbotsch
jakobbotsch requested a review from markplesMay 23, 2023 14:46
@jakobbotsch

Copy link
Copy Markdown
MemberAuthor

Checked offline with @AlekseyTs and this needs to wait for a corresponding VB.NET compiler fix (C# compiler was already fixed). Will close this for now.

@BruceForstall

Copy link
Copy Markdown
Contributor

Checked offline with @AlekseyTs and this needs to wait for a corresponding VB.NET compiler fix (C# compiler was already fixed). Will close this for now.

Don't we need to support IL generated by older (unfixed) compilers?

@jakobbotsch

Copy link
Copy Markdown
MemberAuthor

Don't we need to support IL generated by older (unfixed) compilers?

It's not that we do not support the IL, but currently we have a JIT bug that nondeterministically (depending on numbers of args, debug vs release) can sometimes evaluate things in the wrong order, and it just so happens that there is a VB.NET unit test that relies on one of these cases.
When we met about this we were ok with taking the JIT fix once the C# and VN compilers are fixed to emit correct IL for these patterns.

@ghostghost locked as resolved and limited conversation to collaborators Jul 13, 2023
@jakobbotschjakobbotsch reopened this Apr 8, 2024

if (hasThis && (constraintCallThisTransform == CORINFO_DEREF_THIS))
{
impSpillSideEffects(false, CHECK_SPILL_ALL DEBUGARG(

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.

I don't recall exactly what a "global effect" is in this context. Could the same issue happen if the this pointer was from a loaded field rather than a local?

@jakobbotschjakobbotschApr 8, 2024

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

spillGlobEffects here means to also spill nodes that read heap memory, in addition to all other side effects. We don't need to spill those because inserting an indirection doesn't modify global state.

The same issue can happen if the pointer is from a loaded field, but the spill here is still sufficient (anything that can modify global memory will be spilled even without spillGlobEffects).

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.

👍

Comment on lines +18 to +25
if (Result == 100)
{
Console.WriteLine("PASS");
}
else
{
Console.WriteLine("FAIL: Got result {0}", Result);
}

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.

I think the autogenerated harness will already display failure information.

@jakobbotsch
jakobbotsch merged commit 42f2b33 into dotnet:mainApr 16, 2024
@jakobbotsch
jakobbotsch deleted the fix-73615 branch April 16, 2024 08:23
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 incorrectly reorders constrained call 'this' indirections with other arguments

3 participants

@jakobbotsch@BruceForstall@markples
, '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" + '
JIT: Fix constrained call dereferences happening too early by jakobbotsch · Pull Request #86638 · dotnet/runtime · GitHub
Skip to content

JIT: Fix constrained call dereferences happening too early - #86638

Merged
jakobbotsch merged 3 commits into
dotnet:mainfrom
jakobbotsch:fix-73615
Apr 16, 2024
Merged

JIT: Fix constrained call dereferences happening too early#86638
jakobbotsch merged 3 commits into
dotnet:mainfrom
jakobbotsch:fix-73615

Conversation

@jakobbotsch

Copy link
Copy Markdown
Member

Constrained calls in IL include an implicit dereference that occurs as part of the call. The JIT was adding this dereference on top of the 'this' argument tree, which makes it happen too early (before other arguments are evaluated). This changes the importer to spill when necessary to preserve ordering.

For:

 .method private hidebysig static void Foo(class Runtime_73615/C arg) cil managed noinlining
{
// Code size 21 (0x15)
.maxstack 2
IL_0000: ldarga.s arg
IL_0002: ldarga.s arg
IL_0004: call int32 Runtime_73615::Bar(class Runtime_73615/C&)
IL_0009: constrained. Runtime_73615/C
IL_000f: callvirt instance void Runtime_73615/C::Baz(int32)
IL_0014: ret
}

Before:

*****BB01STMT00000 ( 0x000[E-] ... 0x014 )
[000003] --C-G------CALL nullcheck void Runtime_73615+C:Baz(int):this
[000004] n---G------this├──▌IND ref
[000000] -----------└──▌LCL_ADDR long V00 arg0 [+0]
[000002] --C-G------ arg1 └──▌CALL int Runtime_73615:Bar(byref):int
[000001] ----------- arg0 └──▌LCL_ADDR byref V00 arg0 [+0]

After:

*****BB01STMT00000 ( 0x000[E-] ... 0x014 )
[000005] -AC-G------ASG int
[000004] D------N---├──▌LCL_VAR int V02 tmp1
[000002] --C-G------└──▌CALL int Runtime_73615:Bar(byref):int
[000001] ----------- arg0 └──▌LCL_ADDR byref V00 arg0 [+0]
*****BB01STMT00001 ( ??? ... ??? )
[000003] --C-G------CALL nullcheck void Runtime_73615+C:Baz(int):this
[000007] n---G------this├──▌IND ref
[000000] -----------└──▌LCL_ADDR long V00 arg0 [+0]
[000006] ----------- arg1 └──▌LCL_VAR int V02 tmp1

Fix#73615

Some regressions/improvements are expected due to more spilling in these cases.

Constrained calls in IL include an implicit dereference that occurs as
part of the call. The JIT was adding this dereference on top of the
'this' argument tree, which makes it happen too early (before other
arguments are evaluated). This changes the importer to spill when
necessary to preserve ordering.
For:
```cil
.method private hidebysig static void Foo(class Runtime_73615/C arg) cil managed noinlining
{
// Code size 21 (0x15)
.maxstack 2
IL_0000: ldarga.s arg
IL_0002: ldarga.s arg
IL_0004: call int32 Runtime_73615::Bar(class Runtime_73615/C&)
IL_0009: constrained. Runtime_73615/C
IL_000f: callvirt instance void Runtime_73615/C::Baz(int32)
IL_0014: ret
}
```
Before:
```
***** BB01
STMT00000 ( 0x000[E-] ... 0x014 )
[000003] --C-G------ ▌ CALL nullcheck void Runtime_73615+C:Baz(int):this
[000004] n---G------ this ├──▌ IND ref
[000000] ----------- │ └──▌ LCL_ADDR long V00 arg0 [+0]
[000002] --C-G------ arg1 └──▌ CALL int Runtime_73615:Bar(byref):int
[000001] ----------- arg0 └──▌ LCL_ADDR byref V00 arg0 [+0]
```
After:
```
***** BB01
STMT00000 ( 0x000[E-] ... 0x014 )
[000005] -AC-G------ ▌ ASG int
[000004] D------N--- ├──▌ LCL_VAR int V02 tmp1
[000002] --C-G------ └──▌ CALL int Runtime_73615:Bar(byref):int
[000001] ----------- arg0 └──▌ LCL_ADDR byref V00 arg0 [+0]
***** BB01
STMT00001 ( ??? ... ??? )
[000003] --C-G------ ▌ CALL nullcheck void Runtime_73615+C:Baz(int):this
[000007] n---G------ this ├──▌ IND ref
[000000] ----------- │ └──▌ LCL_ADDR long V00 arg0 [+0]
[000006] ----------- arg1 └──▌ LCL_VAR int V02 tmp1
```
Fixdotnet#73615
@ghostghost added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label May 23, 2023
@ghost

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

Constrained calls in IL include an implicit dereference that occurs as part of the call. The JIT was adding this dereference on top of the 'this' argument tree, which makes it happen too early (before other arguments are evaluated). This changes the importer to spill when necessary to preserve ordering.

For:

 .method private hidebysig static void Foo(class Runtime_73615/C arg) cil managed noinlining
{
// Code size 21 (0x15)
.maxstack 2
IL_0000: ldarga.s arg
IL_0002: ldarga.s arg
IL_0004: call int32 Runtime_73615::Bar(class Runtime_73615/C&)
IL_0009: constrained. Runtime_73615/C
IL_000f: callvirt instance void Runtime_73615/C::Baz(int32)
IL_0014: ret
}

Before:

*****BB01STMT00000 ( 0x000[E-] ... 0x014 )
[000003] --C-G------CALL nullcheck void Runtime_73615+C:Baz(int):this
[000004] n---G------this├──▌IND ref
[000000] -----------└──▌LCL_ADDR long V00 arg0 [+0]
[000002] --C-G------ arg1 └──▌CALL int Runtime_73615:Bar(byref):int
[000001] ----------- arg0 └──▌LCL_ADDR byref V00 arg0 [+0]

After:

*****BB01STMT00000 ( 0x000[E-] ... 0x014 )
[000005] -AC-G------ASG int
[000004] D------N---├──▌LCL_VAR int V02 tmp1
[000002] --C-G------└──▌CALL int Runtime_73615:Bar(byref):int
[000001] ----------- arg0 └──▌LCL_ADDR byref V00 arg0 [+0]
*****BB01STMT00001 ( ??? ... ??? )
[000003] --C-G------CALL nullcheck void Runtime_73615+C:Baz(int):this
[000007] n---G------this├──▌IND ref
[000000] -----------└──▌LCL_ADDR long V00 arg0 [+0]
[000006] ----------- arg1 └──▌LCL_VAR int V02 tmp1

Fix #73615

Some regressions/improvements are expected due to more spilling in these cases.

Author:jakobbotsch
Assignees:-
Labels:

area-CodeGen-coreclr

Milestone:-

@jakobbotsch

Copy link
Copy Markdown
MemberAuthor

Diffs.

cc @dotnet/jit-contrib PTAL @markples

@jakobbotsch
jakobbotsch requested a review from markplesMay 23, 2023 14:46
@jakobbotsch

Copy link
Copy Markdown
MemberAuthor

Checked offline with @AlekseyTs and this needs to wait for a corresponding VB.NET compiler fix (C# compiler was already fixed). Will close this for now.

@BruceForstall

Copy link
Copy Markdown
Contributor

Checked offline with @AlekseyTs and this needs to wait for a corresponding VB.NET compiler fix (C# compiler was already fixed). Will close this for now.

Don't we need to support IL generated by older (unfixed) compilers?

@jakobbotsch

Copy link
Copy Markdown
MemberAuthor

Don't we need to support IL generated by older (unfixed) compilers?

It's not that we do not support the IL, but currently we have a JIT bug that nondeterministically (depending on numbers of args, debug vs release) can sometimes evaluate things in the wrong order, and it just so happens that there is a VB.NET unit test that relies on one of these cases.
When we met about this we were ok with taking the JIT fix once the C# and VN compilers are fixed to emit correct IL for these patterns.

@ghostghost locked as resolved and limited conversation to collaborators Jul 13, 2023
@jakobbotschjakobbotsch reopened this Apr 8, 2024

if (hasThis && (constraintCallThisTransform == CORINFO_DEREF_THIS))
{
impSpillSideEffects(false, CHECK_SPILL_ALL DEBUGARG(

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.

I don't recall exactly what a "global effect" is in this context. Could the same issue happen if the this pointer was from a loaded field rather than a local?

@jakobbotschjakobbotschApr 8, 2024

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

spillGlobEffects here means to also spill nodes that read heap memory, in addition to all other side effects. We don't need to spill those because inserting an indirection doesn't modify global state.

The same issue can happen if the pointer is from a loaded field, but the spill here is still sufficient (anything that can modify global memory will be spilled even without spillGlobEffects).

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.

👍

Comment on lines +18 to +25
if (Result == 100)
{
Console.WriteLine("PASS");
}
else
{
Console.WriteLine("FAIL: Got result {0}", Result);
}

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.

I think the autogenerated harness will already display failure information.

@jakobbotsch
jakobbotsch merged commit 42f2b33 into dotnet:mainApr 16, 2024
@jakobbotsch
jakobbotsch deleted the fix-73615 branch April 16, 2024 08:23
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 incorrectly reorders constrained call 'this' indirections with other arguments

3 participants

@jakobbotsch@BruceForstall@markples
, '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('^' + ".*" + ' JIT: Fix constrained call dereferences happening too early by jakobbotsch · Pull Request #86638 · dotnet/runtime · GitHub
Skip to content

JIT: Fix constrained call dereferences happening too early - #86638

Merged
jakobbotsch merged 3 commits into
dotnet:mainfrom
jakobbotsch:fix-73615
Apr 16, 2024
Merged

JIT: Fix constrained call dereferences happening too early#86638
jakobbotsch merged 3 commits into
dotnet:mainfrom
jakobbotsch:fix-73615

Conversation

@jakobbotsch

Copy link
Copy Markdown
Member

Constrained calls in IL include an implicit dereference that occurs as part of the call. The JIT was adding this dereference on top of the 'this' argument tree, which makes it happen too early (before other arguments are evaluated). This changes the importer to spill when necessary to preserve ordering.

For:

 .method private hidebysig static void Foo(class Runtime_73615/C arg) cil managed noinlining
{
// Code size 21 (0x15)
.maxstack 2
IL_0000: ldarga.s arg
IL_0002: ldarga.s arg
IL_0004: call int32 Runtime_73615::Bar(class Runtime_73615/C&)
IL_0009: constrained. Runtime_73615/C
IL_000f: callvirt instance void Runtime_73615/C::Baz(int32)
IL_0014: ret
}

Before:

*****BB01STMT00000 ( 0x000[E-] ... 0x014 )
[000003] --C-G------CALL nullcheck void Runtime_73615+C:Baz(int):this
[000004] n---G------this├──▌IND ref
[000000] -----------└──▌LCL_ADDR long V00 arg0 [+0]
[000002] --C-G------ arg1 └──▌CALL int Runtime_73615:Bar(byref):int
[000001] ----------- arg0 └──▌LCL_ADDR byref V00 arg0 [+0]

After:

*****BB01STMT00000 ( 0x000[E-] ... 0x014 )
[000005] -AC-G------ASG int
[000004] D------N---├──▌LCL_VAR int V02 tmp1
[000002] --C-G------└──▌CALL int Runtime_73615:Bar(byref):int
[000001] ----------- arg0 └──▌LCL_ADDR byref V00 arg0 [+0]
*****BB01STMT00001 ( ??? ... ??? )
[000003] --C-G------CALL nullcheck void Runtime_73615+C:Baz(int):this
[000007] n---G------this├──▌IND ref
[000000] -----------└──▌LCL_ADDR long V00 arg0 [+0]
[000006] ----------- arg1 └──▌LCL_VAR int V02 tmp1

Fix#73615

Some regressions/improvements are expected due to more spilling in these cases.

Constrained calls in IL include an implicit dereference that occurs as
part of the call. The JIT was adding this dereference on top of the
'this' argument tree, which makes it happen too early (before other
arguments are evaluated). This changes the importer to spill when
necessary to preserve ordering.
For:
```cil
.method private hidebysig static void Foo(class Runtime_73615/C arg) cil managed noinlining
{
// Code size 21 (0x15)
.maxstack 2
IL_0000: ldarga.s arg
IL_0002: ldarga.s arg
IL_0004: call int32 Runtime_73615::Bar(class Runtime_73615/C&)
IL_0009: constrained. Runtime_73615/C
IL_000f: callvirt instance void Runtime_73615/C::Baz(int32)
IL_0014: ret
}
```
Before:
```
***** BB01
STMT00000 ( 0x000[E-] ... 0x014 )
[000003] --C-G------ ▌ CALL nullcheck void Runtime_73615+C:Baz(int):this
[000004] n---G------ this ├──▌ IND ref
[000000] ----------- │ └──▌ LCL_ADDR long V00 arg0 [+0]
[000002] --C-G------ arg1 └──▌ CALL int Runtime_73615:Bar(byref):int
[000001] ----------- arg0 └──▌ LCL_ADDR byref V00 arg0 [+0]
```
After:
```
***** BB01
STMT00000 ( 0x000[E-] ... 0x014 )
[000005] -AC-G------ ▌ ASG int
[000004] D------N--- ├──▌ LCL_VAR int V02 tmp1
[000002] --C-G------ └──▌ CALL int Runtime_73615:Bar(byref):int
[000001] ----------- arg0 └──▌ LCL_ADDR byref V00 arg0 [+0]
***** BB01
STMT00001 ( ??? ... ??? )
[000003] --C-G------ ▌ CALL nullcheck void Runtime_73615+C:Baz(int):this
[000007] n---G------ this ├──▌ IND ref
[000000] ----------- │ └──▌ LCL_ADDR long V00 arg0 [+0]
[000006] ----------- arg1 └──▌ LCL_VAR int V02 tmp1
```
Fixdotnet#73615
@ghostghost added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label May 23, 2023
@ghost

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

Constrained calls in IL include an implicit dereference that occurs as part of the call. The JIT was adding this dereference on top of the 'this' argument tree, which makes it happen too early (before other arguments are evaluated). This changes the importer to spill when necessary to preserve ordering.

For:

 .method private hidebysig static void Foo(class Runtime_73615/C arg) cil managed noinlining
{
// Code size 21 (0x15)
.maxstack 2
IL_0000: ldarga.s arg
IL_0002: ldarga.s arg
IL_0004: call int32 Runtime_73615::Bar(class Runtime_73615/C&)
IL_0009: constrained. Runtime_73615/C
IL_000f: callvirt instance void Runtime_73615/C::Baz(int32)
IL_0014: ret
}

Before:

*****BB01STMT00000 ( 0x000[E-] ... 0x014 )
[000003] --C-G------CALL nullcheck void Runtime_73615+C:Baz(int):this
[000004] n---G------this├──▌IND ref
[000000] -----------└──▌LCL_ADDR long V00 arg0 [+0]
[000002] --C-G------ arg1 └──▌CALL int Runtime_73615:Bar(byref):int
[000001] ----------- arg0 └──▌LCL_ADDR byref V00 arg0 [+0]

After:

*****BB01STMT00000 ( 0x000[E-] ... 0x014 )
[000005] -AC-G------ASG int
[000004] D------N---├──▌LCL_VAR int V02 tmp1
[000002] --C-G------└──▌CALL int Runtime_73615:Bar(byref):int
[000001] ----------- arg0 └──▌LCL_ADDR byref V00 arg0 [+0]
*****BB01STMT00001 ( ??? ... ??? )
[000003] --C-G------CALL nullcheck void Runtime_73615+C:Baz(int):this
[000007] n---G------this├──▌IND ref
[000000] -----------└──▌LCL_ADDR long V00 arg0 [+0]
[000006] ----------- arg1 └──▌LCL_VAR int V02 tmp1

Fix #73615

Some regressions/improvements are expected due to more spilling in these cases.

Author:jakobbotsch
Assignees:-
Labels:

area-CodeGen-coreclr

Milestone:-

@jakobbotsch

Copy link
Copy Markdown
MemberAuthor

Diffs.

cc @dotnet/jit-contrib PTAL @markples

@jakobbotsch
jakobbotsch requested a review from markplesMay 23, 2023 14:46
@jakobbotsch

Copy link
Copy Markdown
MemberAuthor

Checked offline with @AlekseyTs and this needs to wait for a corresponding VB.NET compiler fix (C# compiler was already fixed). Will close this for now.

@BruceForstall

Copy link
Copy Markdown
Contributor

Checked offline with @AlekseyTs and this needs to wait for a corresponding VB.NET compiler fix (C# compiler was already fixed). Will close this for now.

Don't we need to support IL generated by older (unfixed) compilers?

@jakobbotsch

Copy link
Copy Markdown
MemberAuthor

Don't we need to support IL generated by older (unfixed) compilers?

It's not that we do not support the IL, but currently we have a JIT bug that nondeterministically (depending on numbers of args, debug vs release) can sometimes evaluate things in the wrong order, and it just so happens that there is a VB.NET unit test that relies on one of these cases.
When we met about this we were ok with taking the JIT fix once the C# and VN compilers are fixed to emit correct IL for these patterns.

@ghostghost locked as resolved and limited conversation to collaborators Jul 13, 2023
@jakobbotschjakobbotsch reopened this Apr 8, 2024

if (hasThis && (constraintCallThisTransform == CORINFO_DEREF_THIS))
{
impSpillSideEffects(false, CHECK_SPILL_ALL DEBUGARG(

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.

I don't recall exactly what a "global effect" is in this context. Could the same issue happen if the this pointer was from a loaded field rather than a local?

@jakobbotschjakobbotschApr 8, 2024

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

spillGlobEffects here means to also spill nodes that read heap memory, in addition to all other side effects. We don't need to spill those because inserting an indirection doesn't modify global state.

The same issue can happen if the pointer is from a loaded field, but the spill here is still sufficient (anything that can modify global memory will be spilled even without spillGlobEffects).

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.

👍

Comment on lines +18 to +25
if (Result == 100)
{
Console.WriteLine("PASS");
}
else
{
Console.WriteLine("FAIL: Got result {0}", Result);
}

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.

I think the autogenerated harness will already display failure information.

@jakobbotsch
jakobbotsch merged commit 42f2b33 into dotnet:mainApr 16, 2024
@jakobbotsch
jakobbotsch deleted the fix-73615 branch April 16, 2024 08:23
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 incorrectly reorders constrained call 'this' indirections with other arguments

3 participants

@jakobbotsch@BruceForstall@markples
, '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('^' + ".*" + ' JIT: Fix constrained call dereferences happening too early by jakobbotsch · Pull Request #86638 · dotnet/runtime · GitHub
Skip to content

JIT: Fix constrained call dereferences happening too early - #86638

Merged
jakobbotsch merged 3 commits into
dotnet:mainfrom
jakobbotsch:fix-73615
Apr 16, 2024
Merged

JIT: Fix constrained call dereferences happening too early#86638
jakobbotsch merged 3 commits into
dotnet:mainfrom
jakobbotsch:fix-73615

Conversation

@jakobbotsch

Copy link
Copy Markdown
Member

Constrained calls in IL include an implicit dereference that occurs as part of the call. The JIT was adding this dereference on top of the 'this' argument tree, which makes it happen too early (before other arguments are evaluated). This changes the importer to spill when necessary to preserve ordering.

For:

 .method private hidebysig static void Foo(class Runtime_73615/C arg) cil managed noinlining
{
// Code size 21 (0x15)
.maxstack 2
IL_0000: ldarga.s arg
IL_0002: ldarga.s arg
IL_0004: call int32 Runtime_73615::Bar(class Runtime_73615/C&)
IL_0009: constrained. Runtime_73615/C
IL_000f: callvirt instance void Runtime_73615/C::Baz(int32)
IL_0014: ret
}

Before:

*****BB01STMT00000 ( 0x000[E-] ... 0x014 )
[000003] --C-G------CALL nullcheck void Runtime_73615+C:Baz(int):this
[000004] n---G------this├──▌IND ref
[000000] -----------└──▌LCL_ADDR long V00 arg0 [+0]
[000002] --C-G------ arg1 └──▌CALL int Runtime_73615:Bar(byref):int
[000001] ----------- arg0 └──▌LCL_ADDR byref V00 arg0 [+0]

After:

*****BB01STMT00000 ( 0x000[E-] ... 0x014 )
[000005] -AC-G------ASG int
[000004] D------N---├──▌LCL_VAR int V02 tmp1
[000002] --C-G------└──▌CALL int Runtime_73615:Bar(byref):int
[000001] ----------- arg0 └──▌LCL_ADDR byref V00 arg0 [+0]
*****BB01STMT00001 ( ??? ... ??? )
[000003] --C-G------CALL nullcheck void Runtime_73615+C:Baz(int):this
[000007] n---G------this├──▌IND ref
[000000] -----------└──▌LCL_ADDR long V00 arg0 [+0]
[000006] ----------- arg1 └──▌LCL_VAR int V02 tmp1

Fix#73615

Some regressions/improvements are expected due to more spilling in these cases.

Constrained calls in IL include an implicit dereference that occurs as
part of the call. The JIT was adding this dereference on top of the
'this' argument tree, which makes it happen too early (before other
arguments are evaluated). This changes the importer to spill when
necessary to preserve ordering.
For:
```cil
.method private hidebysig static void Foo(class Runtime_73615/C arg) cil managed noinlining
{
// Code size 21 (0x15)
.maxstack 2
IL_0000: ldarga.s arg
IL_0002: ldarga.s arg
IL_0004: call int32 Runtime_73615::Bar(class Runtime_73615/C&)
IL_0009: constrained. Runtime_73615/C
IL_000f: callvirt instance void Runtime_73615/C::Baz(int32)
IL_0014: ret
}
```
Before:
```
***** BB01
STMT00000 ( 0x000[E-] ... 0x014 )
[000003] --C-G------ ▌ CALL nullcheck void Runtime_73615+C:Baz(int):this
[000004] n---G------ this ├──▌ IND ref
[000000] ----------- │ └──▌ LCL_ADDR long V00 arg0 [+0]
[000002] --C-G------ arg1 └──▌ CALL int Runtime_73615:Bar(byref):int
[000001] ----------- arg0 └──▌ LCL_ADDR byref V00 arg0 [+0]
```
After:
```
***** BB01
STMT00000 ( 0x000[E-] ... 0x014 )
[000005] -AC-G------ ▌ ASG int
[000004] D------N--- ├──▌ LCL_VAR int V02 tmp1
[000002] --C-G------ └──▌ CALL int Runtime_73615:Bar(byref):int
[000001] ----------- arg0 └──▌ LCL_ADDR byref V00 arg0 [+0]
***** BB01
STMT00001 ( ??? ... ??? )
[000003] --C-G------ ▌ CALL nullcheck void Runtime_73615+C:Baz(int):this
[000007] n---G------ this ├──▌ IND ref
[000000] ----------- │ └──▌ LCL_ADDR long V00 arg0 [+0]
[000006] ----------- arg1 └──▌ LCL_VAR int V02 tmp1
```
Fixdotnet#73615
@ghostghost added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label May 23, 2023
@ghost

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

Constrained calls in IL include an implicit dereference that occurs as part of the call. The JIT was adding this dereference on top of the 'this' argument tree, which makes it happen too early (before other arguments are evaluated). This changes the importer to spill when necessary to preserve ordering.

For:

 .method private hidebysig static void Foo(class Runtime_73615/C arg) cil managed noinlining
{
// Code size 21 (0x15)
.maxstack 2
IL_0000: ldarga.s arg
IL_0002: ldarga.s arg
IL_0004: call int32 Runtime_73615::Bar(class Runtime_73615/C&)
IL_0009: constrained. Runtime_73615/C
IL_000f: callvirt instance void Runtime_73615/C::Baz(int32)
IL_0014: ret
}

Before:

*****BB01STMT00000 ( 0x000[E-] ... 0x014 )
[000003] --C-G------CALL nullcheck void Runtime_73615+C:Baz(int):this
[000004] n---G------this├──▌IND ref
[000000] -----------└──▌LCL_ADDR long V00 arg0 [+0]
[000002] --C-G------ arg1 └──▌CALL int Runtime_73615:Bar(byref):int
[000001] ----------- arg0 └──▌LCL_ADDR byref V00 arg0 [+0]

After:

*****BB01STMT00000 ( 0x000[E-] ... 0x014 )
[000005] -AC-G------ASG int
[000004] D------N---├──▌LCL_VAR int V02 tmp1
[000002] --C-G------└──▌CALL int Runtime_73615:Bar(byref):int
[000001] ----------- arg0 └──▌LCL_ADDR byref V00 arg0 [+0]
*****BB01STMT00001 ( ??? ... ??? )
[000003] --C-G------CALL nullcheck void Runtime_73615+C:Baz(int):this
[000007] n---G------this├──▌IND ref
[000000] -----------└──▌LCL_ADDR long V00 arg0 [+0]
[000006] ----------- arg1 └──▌LCL_VAR int V02 tmp1

Fix #73615

Some regressions/improvements are expected due to more spilling in these cases.

Author:jakobbotsch
Assignees:-
Labels:

area-CodeGen-coreclr

Milestone:-

@jakobbotsch

Copy link
Copy Markdown
MemberAuthor

Diffs.

cc @dotnet/jit-contrib PTAL @markples

@jakobbotsch
jakobbotsch requested a review from markplesMay 23, 2023 14:46
@jakobbotsch

Copy link
Copy Markdown
MemberAuthor

Checked offline with @AlekseyTs and this needs to wait for a corresponding VB.NET compiler fix (C# compiler was already fixed). Will close this for now.

@BruceForstall

Copy link
Copy Markdown
Contributor

Checked offline with @AlekseyTs and this needs to wait for a corresponding VB.NET compiler fix (C# compiler was already fixed). Will close this for now.

Don't we need to support IL generated by older (unfixed) compilers?

@jakobbotsch

Copy link
Copy Markdown
MemberAuthor

Don't we need to support IL generated by older (unfixed) compilers?

It's not that we do not support the IL, but currently we have a JIT bug that nondeterministically (depending on numbers of args, debug vs release) can sometimes evaluate things in the wrong order, and it just so happens that there is a VB.NET unit test that relies on one of these cases.
When we met about this we were ok with taking the JIT fix once the C# and VN compilers are fixed to emit correct IL for these patterns.

@ghostghost locked as resolved and limited conversation to collaborators Jul 13, 2023
@jakobbotschjakobbotsch reopened this Apr 8, 2024

if (hasThis && (constraintCallThisTransform == CORINFO_DEREF_THIS))
{
impSpillSideEffects(false, CHECK_SPILL_ALL DEBUGARG(

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.

I don't recall exactly what a "global effect" is in this context. Could the same issue happen if the this pointer was from a loaded field rather than a local?

@jakobbotschjakobbotschApr 8, 2024

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

spillGlobEffects here means to also spill nodes that read heap memory, in addition to all other side effects. We don't need to spill those because inserting an indirection doesn't modify global state.

The same issue can happen if the pointer is from a loaded field, but the spill here is still sufficient (anything that can modify global memory will be spilled even without spillGlobEffects).

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.

👍

Comment on lines +18 to +25
if (Result == 100)
{
Console.WriteLine("PASS");
}
else
{
Console.WriteLine("FAIL: Got result {0}", Result);
}

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.

I think the autogenerated harness will already display failure information.

@jakobbotsch
jakobbotsch merged commit 42f2b33 into dotnet:mainApr 16, 2024
@jakobbotsch
jakobbotsch deleted the fix-73615 branch April 16, 2024 08:23
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 incorrectly reorders constrained call 'this' indirections with other arguments

3 participants

@jakobbotsch@BruceForstall@markples
, '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" + ' JIT: Fix constrained call dereferences happening too early by jakobbotsch · Pull Request #86638 · dotnet/runtime · GitHub
Skip to content

JIT: Fix constrained call dereferences happening too early - #86638

Merged
jakobbotsch merged 3 commits into
dotnet:mainfrom
jakobbotsch:fix-73615
Apr 16, 2024
Merged

JIT: Fix constrained call dereferences happening too early#86638
jakobbotsch merged 3 commits into
dotnet:mainfrom
jakobbotsch:fix-73615

Conversation

@jakobbotsch

Copy link
Copy Markdown
Member

Constrained calls in IL include an implicit dereference that occurs as part of the call. The JIT was adding this dereference on top of the 'this' argument tree, which makes it happen too early (before other arguments are evaluated). This changes the importer to spill when necessary to preserve ordering.

For:

 .method private hidebysig static void Foo(class Runtime_73615/C arg) cil managed noinlining
{
// Code size 21 (0x15)
.maxstack 2
IL_0000: ldarga.s arg
IL_0002: ldarga.s arg
IL_0004: call int32 Runtime_73615::Bar(class Runtime_73615/C&)
IL_0009: constrained. Runtime_73615/C
IL_000f: callvirt instance void Runtime_73615/C::Baz(int32)
IL_0014: ret
}

Before:

*****BB01STMT00000 ( 0x000[E-] ... 0x014 )
[000003] --C-G------CALL nullcheck void Runtime_73615+C:Baz(int):this
[000004] n---G------this├──▌IND ref
[000000] -----------└──▌LCL_ADDR long V00 arg0 [+0]
[000002] --C-G------ arg1 └──▌CALL int Runtime_73615:Bar(byref):int
[000001] ----------- arg0 └──▌LCL_ADDR byref V00 arg0 [+0]

After:

*****BB01STMT00000 ( 0x000[E-] ... 0x014 )
[000005] -AC-G------ASG int
[000004] D------N---├──▌LCL_VAR int V02 tmp1
[000002] --C-G------└──▌CALL int Runtime_73615:Bar(byref):int
[000001] ----------- arg0 └──▌LCL_ADDR byref V00 arg0 [+0]
*****BB01STMT00001 ( ??? ... ??? )
[000003] --C-G------CALL nullcheck void Runtime_73615+C:Baz(int):this
[000007] n---G------this├──▌IND ref
[000000] -----------└──▌LCL_ADDR long V00 arg0 [+0]
[000006] ----------- arg1 └──▌LCL_VAR int V02 tmp1

Fix#73615

Some regressions/improvements are expected due to more spilling in these cases.

Constrained calls in IL include an implicit dereference that occurs as
part of the call. The JIT was adding this dereference on top of the
'this' argument tree, which makes it happen too early (before other
arguments are evaluated). This changes the importer to spill when
necessary to preserve ordering.
For:
```cil
.method private hidebysig static void Foo(class Runtime_73615/C arg) cil managed noinlining
{
// Code size 21 (0x15)
.maxstack 2
IL_0000: ldarga.s arg
IL_0002: ldarga.s arg
IL_0004: call int32 Runtime_73615::Bar(class Runtime_73615/C&)
IL_0009: constrained. Runtime_73615/C
IL_000f: callvirt instance void Runtime_73615/C::Baz(int32)
IL_0014: ret
}
```
Before:
```
***** BB01
STMT00000 ( 0x000[E-] ... 0x014 )
[000003] --C-G------ ▌ CALL nullcheck void Runtime_73615+C:Baz(int):this
[000004] n---G------ this ├──▌ IND ref
[000000] ----------- │ └──▌ LCL_ADDR long V00 arg0 [+0]
[000002] --C-G------ arg1 └──▌ CALL int Runtime_73615:Bar(byref):int
[000001] ----------- arg0 └──▌ LCL_ADDR byref V00 arg0 [+0]
```
After:
```
***** BB01
STMT00000 ( 0x000[E-] ... 0x014 )
[000005] -AC-G------ ▌ ASG int
[000004] D------N--- ├──▌ LCL_VAR int V02 tmp1
[000002] --C-G------ └──▌ CALL int Runtime_73615:Bar(byref):int
[000001] ----------- arg0 └──▌ LCL_ADDR byref V00 arg0 [+0]
***** BB01
STMT00001 ( ??? ... ??? )
[000003] --C-G------ ▌ CALL nullcheck void Runtime_73615+C:Baz(int):this
[000007] n---G------ this ├──▌ IND ref
[000000] ----------- │ └──▌ LCL_ADDR long V00 arg0 [+0]
[000006] ----------- arg1 └──▌ LCL_VAR int V02 tmp1
```
Fixdotnet#73615
@ghostghost added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label May 23, 2023
@ghost

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

Constrained calls in IL include an implicit dereference that occurs as part of the call. The JIT was adding this dereference on top of the 'this' argument tree, which makes it happen too early (before other arguments are evaluated). This changes the importer to spill when necessary to preserve ordering.

For:

 .method private hidebysig static void Foo(class Runtime_73615/C arg) cil managed noinlining
{
// Code size 21 (0x15)
.maxstack 2
IL_0000: ldarga.s arg
IL_0002: ldarga.s arg
IL_0004: call int32 Runtime_73615::Bar(class Runtime_73615/C&)
IL_0009: constrained. Runtime_73615/C
IL_000f: callvirt instance void Runtime_73615/C::Baz(int32)
IL_0014: ret
}

Before:

*****BB01STMT00000 ( 0x000[E-] ... 0x014 )
[000003] --C-G------CALL nullcheck void Runtime_73615+C:Baz(int):this
[000004] n---G------this├──▌IND ref
[000000] -----------└──▌LCL_ADDR long V00 arg0 [+0]
[000002] --C-G------ arg1 └──▌CALL int Runtime_73615:Bar(byref):int
[000001] ----------- arg0 └──▌LCL_ADDR byref V00 arg0 [+0]

After:

*****BB01STMT00000 ( 0x000[E-] ... 0x014 )
[000005] -AC-G------ASG int
[000004] D------N---├──▌LCL_VAR int V02 tmp1
[000002] --C-G------└──▌CALL int Runtime_73615:Bar(byref):int
[000001] ----------- arg0 └──▌LCL_ADDR byref V00 arg0 [+0]
*****BB01STMT00001 ( ??? ... ??? )
[000003] --C-G------CALL nullcheck void Runtime_73615+C:Baz(int):this
[000007] n---G------this├──▌IND ref
[000000] -----------└──▌LCL_ADDR long V00 arg0 [+0]
[000006] ----------- arg1 └──▌LCL_VAR int V02 tmp1

Fix #73615

Some regressions/improvements are expected due to more spilling in these cases.

Author:jakobbotsch
Assignees:-
Labels:

area-CodeGen-coreclr

Milestone:-

@jakobbotsch

Copy link
Copy Markdown
MemberAuthor

Diffs.

cc @dotnet/jit-contrib PTAL @markples

@jakobbotsch
jakobbotsch requested a review from markplesMay 23, 2023 14:46
@jakobbotsch

Copy link
Copy Markdown
MemberAuthor

Checked offline with @AlekseyTs and this needs to wait for a corresponding VB.NET compiler fix (C# compiler was already fixed). Will close this for now.

@BruceForstall

Copy link
Copy Markdown
Contributor

Checked offline with @AlekseyTs and this needs to wait for a corresponding VB.NET compiler fix (C# compiler was already fixed). Will close this for now.

Don't we need to support IL generated by older (unfixed) compilers?

@jakobbotsch

Copy link
Copy Markdown
MemberAuthor

Don't we need to support IL generated by older (unfixed) compilers?

It's not that we do not support the IL, but currently we have a JIT bug that nondeterministically (depending on numbers of args, debug vs release) can sometimes evaluate things in the wrong order, and it just so happens that there is a VB.NET unit test that relies on one of these cases.
When we met about this we were ok with taking the JIT fix once the C# and VN compilers are fixed to emit correct IL for these patterns.

@ghostghost locked as resolved and limited conversation to collaborators Jul 13, 2023
@jakobbotschjakobbotsch reopened this Apr 8, 2024

if (hasThis && (constraintCallThisTransform == CORINFO_DEREF_THIS))
{
impSpillSideEffects(false, CHECK_SPILL_ALL DEBUGARG(

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.

I don't recall exactly what a "global effect" is in this context. Could the same issue happen if the this pointer was from a loaded field rather than a local?

@jakobbotschjakobbotschApr 8, 2024

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

spillGlobEffects here means to also spill nodes that read heap memory, in addition to all other side effects. We don't need to spill those because inserting an indirection doesn't modify global state.

The same issue can happen if the pointer is from a loaded field, but the spill here is still sufficient (anything that can modify global memory will be spilled even without spillGlobEffects).

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.

👍

Comment on lines +18 to +25
if (Result == 100)
{
Console.WriteLine("PASS");
}
else
{
Console.WriteLine("FAIL: Got result {0}", Result);
}

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.

I think the autogenerated harness will already display failure information.

@jakobbotsch
jakobbotsch merged commit 42f2b33 into dotnet:mainApr 16, 2024
@jakobbotsch
jakobbotsch deleted the fix-73615 branch April 16, 2024 08:23
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 incorrectly reorders constrained call 'this' indirections with other arguments

3 participants

@jakobbotsch@BruceForstall@markples
, '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('^' + ".*" + ' JIT: Fix constrained call dereferences happening too early by jakobbotsch · Pull Request #86638 · dotnet/runtime · GitHub
Skip to content

JIT: Fix constrained call dereferences happening too early - #86638

Merged
jakobbotsch merged 3 commits into
dotnet:mainfrom
jakobbotsch:fix-73615
Apr 16, 2024
Merged

JIT: Fix constrained call dereferences happening too early#86638
jakobbotsch merged 3 commits into
dotnet:mainfrom
jakobbotsch:fix-73615

Conversation

@jakobbotsch

Copy link
Copy Markdown
Member

Constrained calls in IL include an implicit dereference that occurs as part of the call. The JIT was adding this dereference on top of the 'this' argument tree, which makes it happen too early (before other arguments are evaluated). This changes the importer to spill when necessary to preserve ordering.

For:

 .method private hidebysig static void Foo(class Runtime_73615/C arg) cil managed noinlining
{
// Code size 21 (0x15)
.maxstack 2
IL_0000: ldarga.s arg
IL_0002: ldarga.s arg
IL_0004: call int32 Runtime_73615::Bar(class Runtime_73615/C&)
IL_0009: constrained. Runtime_73615/C
IL_000f: callvirt instance void Runtime_73615/C::Baz(int32)
IL_0014: ret
}

Before:

*****BB01STMT00000 ( 0x000[E-] ... 0x014 )
[000003] --C-G------CALL nullcheck void Runtime_73615+C:Baz(int):this
[000004] n---G------this├──▌IND ref
[000000] -----------└──▌LCL_ADDR long V00 arg0 [+0]
[000002] --C-G------ arg1 └──▌CALL int Runtime_73615:Bar(byref):int
[000001] ----------- arg0 └──▌LCL_ADDR byref V00 arg0 [+0]

After:

*****BB01STMT00000 ( 0x000[E-] ... 0x014 )
[000005] -AC-G------ASG int
[000004] D------N---├──▌LCL_VAR int V02 tmp1
[000002] --C-G------└──▌CALL int Runtime_73615:Bar(byref):int
[000001] ----------- arg0 └──▌LCL_ADDR byref V00 arg0 [+0]
*****BB01STMT00001 ( ??? ... ??? )
[000003] --C-G------CALL nullcheck void Runtime_73615+C:Baz(int):this
[000007] n---G------this├──▌IND ref
[000000] -----------└──▌LCL_ADDR long V00 arg0 [+0]
[000006] ----------- arg1 └──▌LCL_VAR int V02 tmp1

Fix#73615

Some regressions/improvements are expected due to more spilling in these cases.

Constrained calls in IL include an implicit dereference that occurs as
part of the call. The JIT was adding this dereference on top of the
'this' argument tree, which makes it happen too early (before other
arguments are evaluated). This changes the importer to spill when
necessary to preserve ordering.
For:
```cil
.method private hidebysig static void Foo(class Runtime_73615/C arg) cil managed noinlining
{
// Code size 21 (0x15)
.maxstack 2
IL_0000: ldarga.s arg
IL_0002: ldarga.s arg
IL_0004: call int32 Runtime_73615::Bar(class Runtime_73615/C&)
IL_0009: constrained. Runtime_73615/C
IL_000f: callvirt instance void Runtime_73615/C::Baz(int32)
IL_0014: ret
}
```
Before:
```
***** BB01
STMT00000 ( 0x000[E-] ... 0x014 )
[000003] --C-G------ ▌ CALL nullcheck void Runtime_73615+C:Baz(int):this
[000004] n---G------ this ├──▌ IND ref
[000000] ----------- │ └──▌ LCL_ADDR long V00 arg0 [+0]
[000002] --C-G------ arg1 └──▌ CALL int Runtime_73615:Bar(byref):int
[000001] ----------- arg0 └──▌ LCL_ADDR byref V00 arg0 [+0]
```
After:
```
***** BB01
STMT00000 ( 0x000[E-] ... 0x014 )
[000005] -AC-G------ ▌ ASG int
[000004] D------N--- ├──▌ LCL_VAR int V02 tmp1
[000002] --C-G------ └──▌ CALL int Runtime_73615:Bar(byref):int
[000001] ----------- arg0 └──▌ LCL_ADDR byref V00 arg0 [+0]
***** BB01
STMT00001 ( ??? ... ??? )
[000003] --C-G------ ▌ CALL nullcheck void Runtime_73615+C:Baz(int):this
[000007] n---G------ this ├──▌ IND ref
[000000] ----------- │ └──▌ LCL_ADDR long V00 arg0 [+0]
[000006] ----------- arg1 └──▌ LCL_VAR int V02 tmp1
```
Fixdotnet#73615
@ghostghost added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label May 23, 2023
@ghost

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

Constrained calls in IL include an implicit dereference that occurs as part of the call. The JIT was adding this dereference on top of the 'this' argument tree, which makes it happen too early (before other arguments are evaluated). This changes the importer to spill when necessary to preserve ordering.

For:

 .method private hidebysig static void Foo(class Runtime_73615/C arg) cil managed noinlining
{
// Code size 21 (0x15)
.maxstack 2
IL_0000: ldarga.s arg
IL_0002: ldarga.s arg
IL_0004: call int32 Runtime_73615::Bar(class Runtime_73615/C&)
IL_0009: constrained. Runtime_73615/C
IL_000f: callvirt instance void Runtime_73615/C::Baz(int32)
IL_0014: ret
}

Before:

*****BB01STMT00000 ( 0x000[E-] ... 0x014 )
[000003] --C-G------CALL nullcheck void Runtime_73615+C:Baz(int):this
[000004] n---G------this├──▌IND ref
[000000] -----------└──▌LCL_ADDR long V00 arg0 [+0]
[000002] --C-G------ arg1 └──▌CALL int Runtime_73615:Bar(byref):int
[000001] ----------- arg0 └──▌LCL_ADDR byref V00 arg0 [+0]

After:

*****BB01STMT00000 ( 0x000[E-] ... 0x014 )
[000005] -AC-G------ASG int
[000004] D------N---├──▌LCL_VAR int V02 tmp1
[000002] --C-G------└──▌CALL int Runtime_73615:Bar(byref):int
[000001] ----------- arg0 └──▌LCL_ADDR byref V00 arg0 [+0]
*****BB01STMT00001 ( ??? ... ??? )
[000003] --C-G------CALL nullcheck void Runtime_73615+C:Baz(int):this
[000007] n---G------this├──▌IND ref
[000000] -----------└──▌LCL_ADDR long V00 arg0 [+0]
[000006] ----------- arg1 └──▌LCL_VAR int V02 tmp1

Fix #73615

Some regressions/improvements are expected due to more spilling in these cases.

Author:jakobbotsch
Assignees:-
Labels:

area-CodeGen-coreclr

Milestone:-

@jakobbotsch

Copy link
Copy Markdown
MemberAuthor

Diffs.

cc @dotnet/jit-contrib PTAL @markples

@jakobbotsch
jakobbotsch requested a review from markplesMay 23, 2023 14:46
@jakobbotsch

Copy link
Copy Markdown
MemberAuthor

Checked offline with @AlekseyTs and this needs to wait for a corresponding VB.NET compiler fix (C# compiler was already fixed). Will close this for now.

@BruceForstall

Copy link
Copy Markdown
Contributor

Checked offline with @AlekseyTs and this needs to wait for a corresponding VB.NET compiler fix (C# compiler was already fixed). Will close this for now.

Don't we need to support IL generated by older (unfixed) compilers?

@jakobbotsch

Copy link
Copy Markdown
MemberAuthor

Don't we need to support IL generated by older (unfixed) compilers?

It's not that we do not support the IL, but currently we have a JIT bug that nondeterministically (depending on numbers of args, debug vs release) can sometimes evaluate things in the wrong order, and it just so happens that there is a VB.NET unit test that relies on one of these cases.
When we met about this we were ok with taking the JIT fix once the C# and VN compilers are fixed to emit correct IL for these patterns.

@ghostghost locked as resolved and limited conversation to collaborators Jul 13, 2023
@jakobbotschjakobbotsch reopened this Apr 8, 2024

if (hasThis && (constraintCallThisTransform == CORINFO_DEREF_THIS))
{
impSpillSideEffects(false, CHECK_SPILL_ALL DEBUGARG(

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.

I don't recall exactly what a "global effect" is in this context. Could the same issue happen if the this pointer was from a loaded field rather than a local?

@jakobbotschjakobbotschApr 8, 2024

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

spillGlobEffects here means to also spill nodes that read heap memory, in addition to all other side effects. We don't need to spill those because inserting an indirection doesn't modify global state.

The same issue can happen if the pointer is from a loaded field, but the spill here is still sufficient (anything that can modify global memory will be spilled even without spillGlobEffects).

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.

👍

Comment on lines +18 to +25
if (Result == 100)
{
Console.WriteLine("PASS");
}
else
{
Console.WriteLine("FAIL: Got result {0}", Result);
}

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.

I think the autogenerated harness will already display failure information.

@jakobbotsch
jakobbotsch merged commit 42f2b33 into dotnet:mainApr 16, 2024
@jakobbotsch
jakobbotsch deleted the fix-73615 branch April 16, 2024 08:23
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 incorrectly reorders constrained call 'this' indirections with other arguments

3 participants

@jakobbotsch@BruceForstall@markples
, '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('^' + ".*" + ' JIT: Fix constrained call dereferences happening too early by jakobbotsch · Pull Request #86638 · dotnet/runtime · GitHub
Skip to content

JIT: Fix constrained call dereferences happening too early - #86638

Merged
jakobbotsch merged 3 commits into
dotnet:mainfrom
jakobbotsch:fix-73615
Apr 16, 2024
Merged

JIT: Fix constrained call dereferences happening too early#86638
jakobbotsch merged 3 commits into
dotnet:mainfrom
jakobbotsch:fix-73615

Conversation

@jakobbotsch

Copy link
Copy Markdown
Member

Constrained calls in IL include an implicit dereference that occurs as part of the call. The JIT was adding this dereference on top of the 'this' argument tree, which makes it happen too early (before other arguments are evaluated). This changes the importer to spill when necessary to preserve ordering.

For:

 .method private hidebysig static void Foo(class Runtime_73615/C arg) cil managed noinlining
{
// Code size 21 (0x15)
.maxstack 2
IL_0000: ldarga.s arg
IL_0002: ldarga.s arg
IL_0004: call int32 Runtime_73615::Bar(class Runtime_73615/C&)
IL_0009: constrained. Runtime_73615/C
IL_000f: callvirt instance void Runtime_73615/C::Baz(int32)
IL_0014: ret
}

Before:

*****BB01STMT00000 ( 0x000[E-] ... 0x014 )
[000003] --C-G------CALL nullcheck void Runtime_73615+C:Baz(int):this
[000004] n---G------this├──▌IND ref
[000000] -----------└──▌LCL_ADDR long V00 arg0 [+0]
[000002] --C-G------ arg1 └──▌CALL int Runtime_73615:Bar(byref):int
[000001] ----------- arg0 └──▌LCL_ADDR byref V00 arg0 [+0]

After:

*****BB01STMT00000 ( 0x000[E-] ... 0x014 )
[000005] -AC-G------ASG int
[000004] D------N---├──▌LCL_VAR int V02 tmp1
[000002] --C-G------└──▌CALL int Runtime_73615:Bar(byref):int
[000001] ----------- arg0 └──▌LCL_ADDR byref V00 arg0 [+0]
*****BB01STMT00001 ( ??? ... ??? )
[000003] --C-G------CALL nullcheck void Runtime_73615+C:Baz(int):this
[000007] n---G------this├──▌IND ref
[000000] -----------└──▌LCL_ADDR long V00 arg0 [+0]
[000006] ----------- arg1 └──▌LCL_VAR int V02 tmp1

Fix#73615

Some regressions/improvements are expected due to more spilling in these cases.

Constrained calls in IL include an implicit dereference that occurs as
part of the call. The JIT was adding this dereference on top of the
'this' argument tree, which makes it happen too early (before other
arguments are evaluated). This changes the importer to spill when
necessary to preserve ordering.
For:
```cil
.method private hidebysig static void Foo(class Runtime_73615/C arg) cil managed noinlining
{
// Code size 21 (0x15)
.maxstack 2
IL_0000: ldarga.s arg
IL_0002: ldarga.s arg
IL_0004: call int32 Runtime_73615::Bar(class Runtime_73615/C&)
IL_0009: constrained. Runtime_73615/C
IL_000f: callvirt instance void Runtime_73615/C::Baz(int32)
IL_0014: ret
}
```
Before:
```
***** BB01
STMT00000 ( 0x000[E-] ... 0x014 )
[000003] --C-G------ ▌ CALL nullcheck void Runtime_73615+C:Baz(int):this
[000004] n---G------ this ├──▌ IND ref
[000000] ----------- │ └──▌ LCL_ADDR long V00 arg0 [+0]
[000002] --C-G------ arg1 └──▌ CALL int Runtime_73615:Bar(byref):int
[000001] ----------- arg0 └──▌ LCL_ADDR byref V00 arg0 [+0]
```
After:
```
***** BB01
STMT00000 ( 0x000[E-] ... 0x014 )
[000005] -AC-G------ ▌ ASG int
[000004] D------N--- ├──▌ LCL_VAR int V02 tmp1
[000002] --C-G------ └──▌ CALL int Runtime_73615:Bar(byref):int
[000001] ----------- arg0 └──▌ LCL_ADDR byref V00 arg0 [+0]
***** BB01
STMT00001 ( ??? ... ??? )
[000003] --C-G------ ▌ CALL nullcheck void Runtime_73615+C:Baz(int):this
[000007] n---G------ this ├──▌ IND ref
[000000] ----------- │ └──▌ LCL_ADDR long V00 arg0 [+0]
[000006] ----------- arg1 └──▌ LCL_VAR int V02 tmp1
```
Fixdotnet#73615
@ghostghost added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label May 23, 2023
@ghost

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

Constrained calls in IL include an implicit dereference that occurs as part of the call. The JIT was adding this dereference on top of the 'this' argument tree, which makes it happen too early (before other arguments are evaluated). This changes the importer to spill when necessary to preserve ordering.

For:

 .method private hidebysig static void Foo(class Runtime_73615/C arg) cil managed noinlining
{
// Code size 21 (0x15)
.maxstack 2
IL_0000: ldarga.s arg
IL_0002: ldarga.s arg
IL_0004: call int32 Runtime_73615::Bar(class Runtime_73615/C&)
IL_0009: constrained. Runtime_73615/C
IL_000f: callvirt instance void Runtime_73615/C::Baz(int32)
IL_0014: ret
}

Before:

*****BB01STMT00000 ( 0x000[E-] ... 0x014 )
[000003] --C-G------CALL nullcheck void Runtime_73615+C:Baz(int):this
[000004] n---G------this├──▌IND ref
[000000] -----------└──▌LCL_ADDR long V00 arg0 [+0]
[000002] --C-G------ arg1 └──▌CALL int Runtime_73615:Bar(byref):int
[000001] ----------- arg0 └──▌LCL_ADDR byref V00 arg0 [+0]

After:

*****BB01STMT00000 ( 0x000[E-] ... 0x014 )
[000005] -AC-G------ASG int
[000004] D------N---├──▌LCL_VAR int V02 tmp1
[000002] --C-G------└──▌CALL int Runtime_73615:Bar(byref):int
[000001] ----------- arg0 └──▌LCL_ADDR byref V00 arg0 [+0]
*****BB01STMT00001 ( ??? ... ??? )
[000003] --C-G------CALL nullcheck void Runtime_73615+C:Baz(int):this
[000007] n---G------this├──▌IND ref
[000000] -----------└──▌LCL_ADDR long V00 arg0 [+0]
[000006] ----------- arg1 └──▌LCL_VAR int V02 tmp1

Fix #73615

Some regressions/improvements are expected due to more spilling in these cases.

Author:jakobbotsch
Assignees:-
Labels:

area-CodeGen-coreclr

Milestone:-

@jakobbotsch

Copy link
Copy Markdown
MemberAuthor

Diffs.

cc @dotnet/jit-contrib PTAL @markples

@jakobbotsch
jakobbotsch requested a review from markplesMay 23, 2023 14:46
@jakobbotsch

Copy link
Copy Markdown
MemberAuthor

Checked offline with @AlekseyTs and this needs to wait for a corresponding VB.NET compiler fix (C# compiler was already fixed). Will close this for now.

@BruceForstall

Copy link
Copy Markdown
Contributor

Checked offline with @AlekseyTs and this needs to wait for a corresponding VB.NET compiler fix (C# compiler was already fixed). Will close this for now.

Don't we need to support IL generated by older (unfixed) compilers?

@jakobbotsch

Copy link
Copy Markdown
MemberAuthor

Don't we need to support IL generated by older (unfixed) compilers?

It's not that we do not support the IL, but currently we have a JIT bug that nondeterministically (depending on numbers of args, debug vs release) can sometimes evaluate things in the wrong order, and it just so happens that there is a VB.NET unit test that relies on one of these cases.
When we met about this we were ok with taking the JIT fix once the C# and VN compilers are fixed to emit correct IL for these patterns.

@ghostghost locked as resolved and limited conversation to collaborators Jul 13, 2023
@jakobbotschjakobbotsch reopened this Apr 8, 2024

if (hasThis && (constraintCallThisTransform == CORINFO_DEREF_THIS))
{
impSpillSideEffects(false, CHECK_SPILL_ALL DEBUGARG(

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.

I don't recall exactly what a "global effect" is in this context. Could the same issue happen if the this pointer was from a loaded field rather than a local?

@jakobbotschjakobbotschApr 8, 2024

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

spillGlobEffects here means to also spill nodes that read heap memory, in addition to all other side effects. We don't need to spill those because inserting an indirection doesn't modify global state.

The same issue can happen if the pointer is from a loaded field, but the spill here is still sufficient (anything that can modify global memory will be spilled even without spillGlobEffects).

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.

👍

Comment on lines +18 to +25
if (Result == 100)
{
Console.WriteLine("PASS");
}
else
{
Console.WriteLine("FAIL: Got result {0}", Result);
}

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.

I think the autogenerated harness will already display failure information.

@jakobbotsch
jakobbotsch merged commit 42f2b33 into dotnet:mainApr 16, 2024
@jakobbotsch
jakobbotsch deleted the fix-73615 branch April 16, 2024 08:23
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 incorrectly reorders constrained call 'this' indirections with other arguments

3 participants

@jakobbotsch@BruceForstall@markples
, '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); } })(); })(); JIT: Fix constrained call dereferences happening too early by jakobbotsch · Pull Request #86638 · dotnet/runtime · GitHub
Skip to content

JIT: Fix constrained call dereferences happening too early - #86638

Merged
jakobbotsch merged 3 commits into
dotnet:mainfrom
jakobbotsch:fix-73615
Apr 16, 2024
Merged

JIT: Fix constrained call dereferences happening too early#86638
jakobbotsch merged 3 commits into
dotnet:mainfrom
jakobbotsch:fix-73615

Conversation

@jakobbotsch

Copy link
Copy Markdown
Member

Constrained calls in IL include an implicit dereference that occurs as part of the call. The JIT was adding this dereference on top of the 'this' argument tree, which makes it happen too early (before other arguments are evaluated). This changes the importer to spill when necessary to preserve ordering.

For:

 .method private hidebysig static void Foo(class Runtime_73615/C arg) cil managed noinlining
{
// Code size 21 (0x15)
.maxstack 2
IL_0000: ldarga.s arg
IL_0002: ldarga.s arg
IL_0004: call int32 Runtime_73615::Bar(class Runtime_73615/C&)
IL_0009: constrained. Runtime_73615/C
IL_000f: callvirt instance void Runtime_73615/C::Baz(int32)
IL_0014: ret
}

Before:

*****BB01STMT00000 ( 0x000[E-] ... 0x014 )
[000003] --C-G------CALL nullcheck void Runtime_73615+C:Baz(int):this
[000004] n---G------this├──▌IND ref
[000000] -----------└──▌LCL_ADDR long V00 arg0 [+0]
[000002] --C-G------ arg1 └──▌CALL int Runtime_73615:Bar(byref):int
[000001] ----------- arg0 └──▌LCL_ADDR byref V00 arg0 [+0]

After:

*****BB01STMT00000 ( 0x000[E-] ... 0x014 )
[000005] -AC-G------ASG int
[000004] D------N---├──▌LCL_VAR int V02 tmp1
[000002] --C-G------└──▌CALL int Runtime_73615:Bar(byref):int
[000001] ----------- arg0 └──▌LCL_ADDR byref V00 arg0 [+0]
*****BB01STMT00001 ( ??? ... ??? )
[000003] --C-G------CALL nullcheck void Runtime_73615+C:Baz(int):this
[000007] n---G------this├──▌IND ref
[000000] -----------└──▌LCL_ADDR long V00 arg0 [+0]
[000006] ----------- arg1 └──▌LCL_VAR int V02 tmp1

Fix#73615

Some regressions/improvements are expected due to more spilling in these cases.

Constrained calls in IL include an implicit dereference that occurs as
part of the call. The JIT was adding this dereference on top of the
'this' argument tree, which makes it happen too early (before other
arguments are evaluated). This changes the importer to spill when
necessary to preserve ordering.
For:
```cil
.method private hidebysig static void Foo(class Runtime_73615/C arg) cil managed noinlining
{
// Code size 21 (0x15)
.maxstack 2
IL_0000: ldarga.s arg
IL_0002: ldarga.s arg
IL_0004: call int32 Runtime_73615::Bar(class Runtime_73615/C&)
IL_0009: constrained. Runtime_73615/C
IL_000f: callvirt instance void Runtime_73615/C::Baz(int32)
IL_0014: ret
}
```
Before:
```
***** BB01
STMT00000 ( 0x000[E-] ... 0x014 )
[000003] --C-G------ ▌ CALL nullcheck void Runtime_73615+C:Baz(int):this
[000004] n---G------ this ├──▌ IND ref
[000000] ----------- │ └──▌ LCL_ADDR long V00 arg0 [+0]
[000002] --C-G------ arg1 └──▌ CALL int Runtime_73615:Bar(byref):int
[000001] ----------- arg0 └──▌ LCL_ADDR byref V00 arg0 [+0]
```
After:
```
***** BB01
STMT00000 ( 0x000[E-] ... 0x014 )
[000005] -AC-G------ ▌ ASG int
[000004] D------N--- ├──▌ LCL_VAR int V02 tmp1
[000002] --C-G------ └──▌ CALL int Runtime_73615:Bar(byref):int
[000001] ----------- arg0 └──▌ LCL_ADDR byref V00 arg0 [+0]
***** BB01
STMT00001 ( ??? ... ??? )
[000003] --C-G------ ▌ CALL nullcheck void Runtime_73615+C:Baz(int):this
[000007] n---G------ this ├──▌ IND ref
[000000] ----------- │ └──▌ LCL_ADDR long V00 arg0 [+0]
[000006] ----------- arg1 └──▌ LCL_VAR int V02 tmp1
```
Fixdotnet#73615
@ghostghost added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label May 23, 2023
@ghost

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

Constrained calls in IL include an implicit dereference that occurs as part of the call. The JIT was adding this dereference on top of the 'this' argument tree, which makes it happen too early (before other arguments are evaluated). This changes the importer to spill when necessary to preserve ordering.

For:

 .method private hidebysig static void Foo(class Runtime_73615/C arg) cil managed noinlining
{
// Code size 21 (0x15)
.maxstack 2
IL_0000: ldarga.s arg
IL_0002: ldarga.s arg
IL_0004: call int32 Runtime_73615::Bar(class Runtime_73615/C&)
IL_0009: constrained. Runtime_73615/C
IL_000f: callvirt instance void Runtime_73615/C::Baz(int32)
IL_0014: ret
}

Before:

*****BB01STMT00000 ( 0x000[E-] ... 0x014 )
[000003] --C-G------CALL nullcheck void Runtime_73615+C:Baz(int):this
[000004] n---G------this├──▌IND ref
[000000] -----------└──▌LCL_ADDR long V00 arg0 [+0]
[000002] --C-G------ arg1 └──▌CALL int Runtime_73615:Bar(byref):int
[000001] ----------- arg0 └──▌LCL_ADDR byref V00 arg0 [+0]

After:

*****BB01STMT00000 ( 0x000[E-] ... 0x014 )
[000005] -AC-G------ASG int
[000004] D------N---├──▌LCL_VAR int V02 tmp1
[000002] --C-G------└──▌CALL int Runtime_73615:Bar(byref):int
[000001] ----------- arg0 └──▌LCL_ADDR byref V00 arg0 [+0]
*****BB01STMT00001 ( ??? ... ??? )
[000003] --C-G------CALL nullcheck void Runtime_73615+C:Baz(int):this
[000007] n---G------this├──▌IND ref
[000000] -----------└──▌LCL_ADDR long V00 arg0 [+0]
[000006] ----------- arg1 └──▌LCL_VAR int V02 tmp1

Fix #73615

Some regressions/improvements are expected due to more spilling in these cases.

Author:jakobbotsch
Assignees:-
Labels:

area-CodeGen-coreclr

Milestone:-

@jakobbotsch

Copy link
Copy Markdown
MemberAuthor

Diffs.

cc @dotnet/jit-contrib PTAL @markples

@jakobbotsch
jakobbotsch requested a review from markplesMay 23, 2023 14:46
@jakobbotsch

Copy link
Copy Markdown
MemberAuthor

Checked offline with @AlekseyTs and this needs to wait for a corresponding VB.NET compiler fix (C# compiler was already fixed). Will close this for now.

@BruceForstall

Copy link
Copy Markdown
Contributor

Checked offline with @AlekseyTs and this needs to wait for a corresponding VB.NET compiler fix (C# compiler was already fixed). Will close this for now.

Don't we need to support IL generated by older (unfixed) compilers?

@jakobbotsch

Copy link
Copy Markdown
MemberAuthor

Don't we need to support IL generated by older (unfixed) compilers?

It's not that we do not support the IL, but currently we have a JIT bug that nondeterministically (depending on numbers of args, debug vs release) can sometimes evaluate things in the wrong order, and it just so happens that there is a VB.NET unit test that relies on one of these cases.
When we met about this we were ok with taking the JIT fix once the C# and VN compilers are fixed to emit correct IL for these patterns.

@ghostghost locked as resolved and limited conversation to collaborators Jul 13, 2023
@jakobbotschjakobbotsch reopened this Apr 8, 2024

if (hasThis && (constraintCallThisTransform == CORINFO_DEREF_THIS))
{
impSpillSideEffects(false, CHECK_SPILL_ALL DEBUGARG(

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.

I don't recall exactly what a "global effect" is in this context. Could the same issue happen if the this pointer was from a loaded field rather than a local?

@jakobbotschjakobbotschApr 8, 2024

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

spillGlobEffects here means to also spill nodes that read heap memory, in addition to all other side effects. We don't need to spill those because inserting an indirection doesn't modify global state.

The same issue can happen if the pointer is from a loaded field, but the spill here is still sufficient (anything that can modify global memory will be spilled even without spillGlobEffects).

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.

👍

Comment on lines +18 to +25
if (Result == 100)
{
Console.WriteLine("PASS");
}
else
{
Console.WriteLine("FAIL: Got result {0}", Result);
}

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.

I think the autogenerated harness will already display failure information.

@jakobbotsch
jakobbotsch merged commit 42f2b33 into dotnet:mainApr 16, 2024
@jakobbotsch
jakobbotsch deleted the fix-73615 branch April 16, 2024 08:23
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 incorrectly reorders constrained call 'this' indirections with other arguments

3 participants

@jakobbotsch@BruceForstall@markples