Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JIT incorrectly reorders constrained call 'this' indirections with other arguments #73615

Closed
jakobbotsch opened this issue Aug 9, 2022 · 5 comments · Fixed by #86638
Closed
Assignees
Labels
area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI bug in-pr There is an active PR which will close this issue when it is merged Priority:3 Work that is nice to have
Milestone

Comments

@jakobbotsch
Copy link
Member

jakobbotsch commented Aug 9, 2022

Small example:

using InlineIL;
using System;
using System.Runtime.CompilerServices;

public class Program
{
    public static void Main()
    {
        Foo(new C("A"));
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    private static void Foo(C arg)
    {
        IL.Emit.Ldarga(nameof(arg));
        IL.Emit.Ldarga(nameof(arg));
        IL.Emit.Call(new MethodRef(typeof(Program), nameof(Bar)));
        IL.Emit.Constrained<C>();
        IL.Emit.Callvirt(new MethodRef(typeof(C), nameof(arg.Baz)));
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    private static int Bar(ref C o)
    {
        o = new C("B");
        return 0;
    }
}

public record class C(string Name)
{
    public void Baz(int arg)
    {
        Console.WriteLine(Name);
    }
}

Expected: B
Actual: A

Seen in #73606 (comment)

category:correctness
theme:importer
skill-level:intermediate
cost:small
impact:small

@dotnet-issue-labeler
Copy link

I couldn't figure out the best area label to add to this issue. If you have write-permissions please help me learn by adding exactly one area label.

@ghost ghost added the untriaged New issue has not been triaged by the area owner label Aug 9, 2022
@jakobbotsch jakobbotsch added area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI and removed untriaged New issue has not been triaged by the area owner labels Aug 9, 2022
@ghost
Copy link

ghost commented Aug 9, 2022

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

Issue Details

Small example:

using InlineIL;
using System;

public class Program
{
    public static void Main()
    {
        Foo(new C("A"));
    }

    private static void Foo(C arg)
    {
        IL.Emit.Ldarga(nameof(arg));
        IL.Emit.Ldarga(nameof(arg));
        IL.Emit.Call(new MethodRef(typeof(Program), nameof(Bar)));
        IL.Emit.Constrained<C>();
        IL.Emit.Callvirt(new MethodRef(typeof(C), nameof(arg.Baz)));
    }

    private static int Bar(ref C o)
    {
        o = new C("B");
        return 0;
    }
}

public record class C(string Name)
{
    public void Baz(int arg)
    {
        Console.WriteLine(Name);
    }
}

Expected: B
Actual: A

Seen in #73606 (comment)

Author: jakobbotsch
Assignees: -
Labels:

area-CodeGen-coreclr

Milestone: -

@jakobbotsch jakobbotsch added this to the 8.0.0 milestone Aug 9, 2022
@jakobbotsch jakobbotsch added the bug label Aug 9, 2022
jakobbotsch added a commit to jakobbotsch/runtime that referenced this issue May 23, 2023
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
```

Fix dotnet#73615
@ghost ghost added in-pr There is an active PR which will close this issue when it is merged and removed in-pr There is an active PR which will close this issue when it is merged labels May 23, 2023
@jakobbotsch jakobbotsch added the blocked Issue/PR is blocked on something - see comments label May 23, 2023
@jakobbotsch
Copy link
Member Author

This needs to wait for dotnet/roslyn#63221 for a runtime side fix. As of writing this the C# compiler has been fixed, but not the VB.NET compiler.

@jakobbotsch jakobbotsch added the Priority:3 Work that is nice to have label Jun 20, 2023
@jakobbotsch
Copy link
Member Author

I'm going to move this to 9.0 since it's blocked waiting for a Roslyn VB.NET fix.

@jakobbotsch jakobbotsch modified the milestones: 8.0.0, 9.0.0 Jul 24, 2023
@AlekseyTs
Copy link
Contributor

@jakobbotsch FYI, the fix for dotnet/roslyn#63221 for VB got merged into Roslyn.

@dotnet-policy-service dotnet-policy-service bot added the in-pr There is an active PR which will close this issue when it is merged label Apr 8, 2024
@jakobbotsch jakobbotsch removed the blocked Issue/PR is blocked on something - see comments label Apr 8, 2024
jakobbotsch added a commit that referenced this issue Apr 16, 2024
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
```

Fix #73615
matouskozak pushed a commit to matouskozak/runtime that referenced this issue Apr 30, 2024
)

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
```

Fix dotnet#73615
@github-actions github-actions bot locked and limited conversation to collaborators May 16, 2024
Ruihan-Yin pushed a commit to Ruihan-Yin/runtime that referenced this issue May 30, 2024
)

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
```

Fix dotnet#73615
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI bug in-pr There is an active PR which will close this issue when it is merged Priority:3 Work that is nice to have
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants