Skip to content

Inlined struct copies via params, returns and assignment not elided #12219

@benaadams

Description

@benaadams

As seen in ValueTaskAwaiter from dotnet/coreclr#22735, dotnet/coreclr#22738 wasn't a specific issue for it with simple repo so opening this.

using System.Runtime.CompilerServices;

class Program
{
    static void Main(string[] args)
    {
        InlinedAssignment();
        InlinedCtor();
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    static long InlinedAssignment()
    {
        var s = CreateLargeStruct();
        s = GetLargeStruct(s);

        return s.l3;
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    static long InlinedCtor()
    {
        var s = new LargeStruct2(CreateLargeStruct());

        return s._largeStruct.l3;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    static LargeStruct GetLargeStruct(LargeStruct l) => l;

    [MethodImpl(MethodImplOptions.NoInlining)]
    static LargeStruct CreateLargeStruct() => new LargeStruct();
}

readonly struct LargeStruct
{
    public readonly long l0;
    public readonly long l1;
    public readonly long l2;
    public readonly long l3;
}

readonly struct LargeStruct2
{
    public readonly LargeStruct _largeStruct;

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public LargeStruct2(LargeStruct largeStruct)
    {
        _largeStruct = largeStruct;
    }
}

Produces

; Assembly listing for method Program:InlinedAssignment():long
;  V00 OutArgs      [V00    ] (  1,  1   )  lclBlk (32) [rsp+0x00]   "OutgoingArgSpace"
;  V01 tmp1         [V01    ] (  2,  4   )  struct (32) [rsp+0x48]   do-not-enreg[XSB] addr-exposed "struct address for call/obj"
;* V02 tmp2         [V02    ] (  0,  0   )  struct (32) zero-ref    "struct address for call/obj"
;  V03 tmp3         [V03    ] (  2,  4   )  struct (32) [rsp+0x28]   do-not-enreg[XSB] addr-exposed "Inlining Arg"
;* V04 tmp4         [V04    ] (  0,  0   )    long  ->  zero-ref    V02.l0(offs=0x00) P-INDEP "field V02.l0 (fldOffset=0x0)"
;* V05 tmp5         [V05    ] (  0,  0   )    long  ->  zero-ref    V02.l1(offs=0x08) P-INDEP "field V02.l1 (fldOffset=0x8)"
;* V06 tmp6         [V06    ] (  0,  0   )    long  ->  zero-ref    V02.l2(offs=0x10) P-INDEP "field V02.l2 (fldOffset=0x10)"
;  V07 tmp7         [V07,T01] (  2,  2   )    long  ->  rax         V02.l3(offs=0x18) P-INDEP "field V02.l3 (fldOffset=0x18)"
;  V08 tmp8         [V08,T00] (  3,  6   )   byref  ->  rax         "BlockOp address local"
; Lcl frame size = 104

G_M60975_IG01:
       4883EC68             sub      rsp, 104
       C5F877               vzeroupper 

G_M60975_IG02:
       488D4C2448           lea      rcx, bword ptr [rsp+48H]
       E8BF67FFFF           call     Program:CreateLargeStruct():struct
       C5FA6F442448         vmovdqu  xmm0, qword ptr [rsp+48H]
       C5FA7F442428         vmovdqu  qword ptr [rsp+28H], xmm0
       C5FA6F442458         vmovdqu  xmm0, qword ptr [rsp+58H]
       C5FA7F442438         vmovdqu  qword ptr [rsp+38H], xmm0
       488D442428           lea      rax, bword ptr [rsp+28H]
       488B10               mov      rdx, qword ptr [rax]
       488B4018             mov      rax, qword ptr [rax+24]

G_M60975_IG03:
       4883C468             add      rsp, 104
       C3                   ret      

; Total bytes of code 58, prolog size 7 for method Program:InlinedAssignment():long

and

; Assembly listing for method Program:InlinedCtor():long
;  V00 OutArgs      [V00    ] (  1,  1   )  lclBlk (32) [rsp+0x00]   "OutgoingArgSpace"
;  V01 tmp1         [V01,T00] (  2,  4   )  struct (32) [rsp+0x68]   do-not-enreg[SFB] "NewObj constructor temp"
;  V02 tmp2         [V02    ] (  2,  4   )  struct (32) [rsp+0x48]   do-not-enreg[XSB] addr-exposed "struct address for call/obj"
;  V03 tmp3         [V03,T01] (  2,  4   )  struct (32) [rsp+0x28]   do-not-enreg[SB] "Inlining Arg"
;
; Lcl frame size = 136

G_M31928_IG01:
       4881EC88000000       sub      rsp, 136
       C5F877               vzeroupper 

G_M31928_IG02:
       488D4C2448           lea      rcx, bword ptr [rsp+48H]
       E8BCFFFFFF           call     Program:CreateLargeStruct():struct
       C5FA6F442448         vmovdqu  xmm0, qword ptr [rsp+48H]
       C5FA7F442428         vmovdqu  qword ptr [rsp+28H], xmm0
       C5FA6F442458         vmovdqu  xmm0, qword ptr [rsp+58H]
       C5FA7F442438         vmovdqu  qword ptr [rsp+38H], xmm0
       C5FA6F442428         vmovdqu  xmm0, qword ptr [rsp+28H]
       C5FA7F442468         vmovdqu  qword ptr [rsp+68H], xmm0
       C5FA6F442438         vmovdqu  xmm0, qword ptr [rsp+38H]
       C5FA7F442478         vmovdqu  qword ptr [rsp+78H], xmm0
       488B842480000000     mov      rax, qword ptr [rsp+80H]

G_M31928_IG03:
       4881C488000000       add      rsp, 136
       C3                   ret      

; Total bytes of code 84, prolog size 10 for method Program:InlinedCtor():long

Some of these copies could be skipped?

/cc @AndyAyersMS @mikedn @stephentoub @jkotas

category:cq
theme:structs
skill-level:expert
cost:large

Metadata

Metadata

Assignees

No one assigned

    Labels

    area-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMIenhancementProduct code improvement that does NOT require public API changes/additionsoptimization

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions