dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
14.94k stars 4.64k forks source link

JIT generates different code for pointer and reference arguments #99759

Open oleg-st opened 6 months ago

oleg-st commented 6 months ago

Description

I have code ported from C that contains passing arguments by pointers, I noticed that if I replace them with passing by reference, it can improve codegeneration.

Code:

using System;

public unsafe class C {   
    public unsafe struct BIT_DStream_t
    {
        public nuint bitContainer;
        public uint bitsConsumed;
        public sbyte* ptr;
        public sbyte* start;
        public sbyte* limitPtr;
    }

    public unsafe struct seqState_t
    {
        public BIT_DStream_t DStream;
    }

    public void Add(BIT_DStream_t* x)
    {
        x->bitContainer++;
        x->bitsConsumed++;
    }

    public void Add(ref BIT_DStream_t x)
    {
        x.bitContainer++;
        x.bitsConsumed++;
    }

    public nuint Test1()
    {
        var x = new seqState_t();
        for (var i = 0; i < 100; i++)
        {
            Add(&x.DStream);
        }

        return x.DStream.bitContainer;
    }

    public nuint Test2()
    {
        var x = new seqState_t();
        for (var i = 0; i < 100; i++)
        {
            Add(ref x.DStream);
        }

        return x.DStream.bitContainer;
    }
}

Test1:

; Method TestConsole8.C:Test1():ulong:this (FullOpts)
G_M000_IG01:
       sub      rsp, 120
       xor      eax, eax
       mov      qword ptr [rsp+0x08], rax
       vxorps   xmm4, xmm4, xmm4
       mov      rax, -96
       vmovdqa  xmmword ptr [rsp+rax+0x70], xmm4
       vmovdqa  xmmword ptr [rsp+rax+0x80], xmm4
       vmovdqa  xmmword ptr [rsp+rax+0x90], xmm4
       add      rax, 48
       jne      SHORT  -5 instr
       mov      qword ptr [rsp+0x70], rax

G_M000_IG02:
       xor      eax, eax
       align    [2 bytes for IG03]

G_M000_IG03:
       lea      rcx, bword ptr [rsp+0x08]
       inc      qword ptr [rcx]
       add      rcx, 8
       inc      dword ptr [rcx]
       inc      eax
       cmp      eax, 100
       jl       SHORT G_M000_IG03

G_M000_IG04:
       mov      rax, qword ptr [rsp+0x08]

G_M000_IG05:
       add      rsp, 120
       ret      
; Total bytes of code: 95

Test2:

; Method TestConsole8.C:Test2():ulong:this (FullOpts)
G_M000_IG01:

G_M000_IG02:
       xor      eax, eax
       xor      ecx, ecx
       xor      edx, edx
       align    [0 bytes for IG03]

G_M000_IG03:
       inc      rax
       inc      ecx
       inc      edx
       cmp      edx, 100
       jl       SHORT G_M000_IG03

G_M000_IG04:
       ret      
; Total bytes of code: 19

SharpLab

Configuration

.NET 8.0.202

dotnet-policy-service[bot] commented 6 months ago

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

hez2010 commented 6 months ago

It's expected currently as pointers are always considered as address-exposed. I wonder if maybe such unnecessary address exposure can be eliminated by the JIT in the future.

jakobbotsch commented 6 months ago

This is not expected. There should be no difference in codegen here -- we end up with a cast on top of an IR node taking the address of the local (already questionable as such casts essentially are no-ops in the JIT), and it blocks a bunch of optimizations.

jakobbotsch commented 1 month ago

Won't get to this CQ enhancement in .NET 9, sadly.