Skip to content

Order of initialization in async kickoff method with params blocks Jit zero init eliding #45295

@benaadams

Description

@benaadams

Currently Rosyln zeros the the AsyncMethodBuilder field with .locals init and then assigns the result of .Create to that field.

When .Create is inlined it is also zeros; so the assignment is a second set of zeroing.

The Jit has an optimization where it will remove the second set of zeroing dotnet/runtime#36918 ("Optimization to remove redundant zero initializations"); however this only kicks in if there aren't any preceding non-zero assignments.

e.g.

using System.IO.Pipelines;

public async ValueTask<ReadResult> OuterMethod(object o)
{
    return await InnerMethod(o);
    
    static async ValueTask<ReadResult> InnerMethod(object o) 
    {
        return default;
    }
}
G_M51821_IG01:
       push     rsi
       sub      rsp, 144
       vzeroupper                                ; zero
       xor      rax, rax                         ;  |
       mov      qword ptr [rsp+28H], rax         ;  |
       vxorps   xmm4, xmm4                       ;  |
       mov      rax, -96                         ;  |
       vmovdqa  xmmword ptr [rsp+rax+90H], xmm4  ;  |
       vmovdqa  xmmword ptr [rsp+rax+A0H], xmm4  ;  |
       vmovdqa  xmmword ptr [rsp+rax+B0H], xmm4  ;  |
       add      rax, 48                          ;  |
       jne      SHORT  -5 instr                  ;  V
       mov      rsi, rdx
						;; bbWeight=1    PerfScore 8.58
G_M51821_IG02:
       mov      gword ptr [rsp+28H], r8      ; save param ** blocks redundant zero elimination **
       xor      ecx, ecx                     ; re-zero
       vxorps   xmm0, xmm0                   ;    |
       vmovdqu  xmmword ptr [rsp+38H], xmm0  ;    |
       vmovdqu  xmmword ptr [rsp+48H], xmm0  ;    |
       mov      qword ptr [rsp+58H], rcx     ;    V
       mov      dword ptr [rsp+30H], -1
       lea      rcx, [rsp+28H]
       call     System.Runtime.CompilerServices.AsyncMethodBuilderCore:Start(byref)
       lea      rcx, bword ptr [rsp+38H]
       mov      rdx, rsi
       call     System.Runtime.CompilerServices.AsyncValueTaskMethodBuilder`1[ReadResult][System.IO.Pipelines.ReadResult]:get_Task():System.Threading.Tasks.ValueTask`1[ReadResult]:this
       mov      rax, rsi

If the call to .Create() was moved to the first step; prior to saving parameters to the fields so it should not block the redundant zero elimination optimization.

From dotnet/runtime#2325 (comment)

Regarding your second example where we first assign to a gc field and then initialize the struct field: currently my optimization doesn't handle that. It's possible to extend it to handle that case but that would require keeping track of field initializations and may be a little tricky.

However, now the answer to your question

Or would changing it to move all the zeroing above that first assignment to non-zero (which would be a much more complex refactoring in Roslyn) help?

is yes.

If the struct field zero initialization was done before the first assignment, it would be eliminated by the new optimization.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Area-CompilersBugCode Gen QualityRoom for improvement in the quality of the compiler's generated code

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions