Skip to content

JIT: Last-use copy elision based on ref counts and liveness interact in incorrect ways #81019

Closed
@jakobbotsch

Description

@jakobbotsch
using System;

class Program
{
    static void Main()
    {
        Foo(default);
    }

    static void Foo(S16 s)
    {
        Baz(s, Bar(s));
    }

    static int Bar(S16 s)
    {
        s.A = 1234;
        Consume(s);
        return 42;
    }

    static void Baz(S16 s, int arg)
    {
        Console.WriteLine(s.A);
    }

    static void Consume(S16 s) { }

    struct S16
    {
        public ushort A, B, C, D, E, F, G, H;
    }
}

Run with DOTNET_JitNoInline=1 on win-x64.
Expected output: 0
Actual output: 1234

The last use of s occurs in the call to Baz so the new last-use based copy elision assumes it can omit the copy. On the other hand, the old ref-count alias-analysis based copy elision assumes that no copy elision can happen in Bar and elides the first copy:

fgMorphTree BB03, STMT00018 (before)
               [000094] --C-G------                           CALL      void   Program:Baz(Program+S16,int)
               [000096] ----------- arg0                    ├──▌  LCL_VAR   struct V00 arg0         
               [000091] --C-G------ arg1                    └──▌  CALL      int    Program:Bar(Program+S16):int
               [000093] ----------- arg0                       └──▌  LCL_VAR   struct V00 arg0          (last use)
Initializing arg info for 94.CALL:
Args for call [000094] CALL after AddFinalArgsAndDetermineABIInfo:
CallArg[[000096].LCL_VAR struct (By ref), 1 reg: rcx, byteAlignment=8, isStruct]
CallArg[[000091].CALL int (By value), 1 reg: rdx, byteAlignment=8]

Arg [000096] is unpromoted implicit byref V00, seeing if we can still tail call
... all 2 appearance(s) are as implicit byref args to calls.
... Running alias analysis on this call's args
... checking other arg [000091]...
               [000091] --C-G------                           CALL      int    Program:Bar(Program+S16):int
               [000093] ----------- arg0                    └──▌  LCL_VAR   struct V00 arg0          (last use)
...arg is not a byref or implicit byref (int)
... yes, no other arg in call can alias V00
[Fast tailcall decision]: Will fast tailcall

...

fgMorphTree BB03, STMT00018 (after)
               [000094] --CXG+-----                           CALL      void   Program:Baz(Program+S16,int)
               [000102] -ACXG------ arg1 setup              ├──▌  ASG       int   
               [000101] D------N---                           ├──▌  LCL_VAR   int    V19 tmp18        
               [000091] --CXG+-----                           └──▌  CALL      int    Program:Bar(Program+S16):int
               [000093] -----+----- arg0 in rcx                  └──▌  LCL_VAR   byref  V00 arg0          (last use)
               [000103] ----------- arg1 in rdx             ├──▌  LCL_VAR   int    V19 tmp18        
               [000096] -----+----- arg0 in rcx             └──▌  LCL_VAR   byref  V00 arg0         

We either need to teach the ref-count version to take these new candidates into account, have it invalidate the last-use information, or disable/remove it entirely.

cc @dotnet/jit-contrib

Metadata

Metadata

Assignees

Labels

area-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions