Closed
Description
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