dotnet / runtime

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

Optimize modification of immutable struct #104209

Open PavelCibulka opened 3 months ago

PavelCibulka commented 3 months ago

When modifying immutable struct (by creating new one and coppying everything except some fields) generated code contains unnecessary instructions.

Examples (.Net 8) Immutable struct code:

    public readonly struct StarData (float mass, float mDot, float logMsolLsol, float tSol) {
        public readonly float mass= mass;
        public readonly float mDot = mDot;
        public readonly float logMsolLsol = logMsolLsol;
        public readonly float tSol = tSol;

        public static StarData SetTsol(StarData s, float tSol){
            return new(
                s.mass,
                s.mDot,
                s.logMsolLsol,
                tSol);
        }
    }

generated code for SetTsol function (https://godbolt.org/ .NET 8.0)

StarData:SetTsol(StarData,float):StarData (FullOpts):
G_M19045_IG01:  ;; offset=0x0000
       sub      rsp, 40
       vzeroupper 
       vmovsd   qword ptr [rsp+0x18], xmm0
       vmovsd   qword ptr [rsp+0x20], xmm1
G_M19045_IG02:  ;; offset=0x0013
       vmovss   xmm0, dword ptr [rsp+0x18]
       vmovss   xmm1, dword ptr [rsp+0x1C]
       vmovss   xmm3, dword ptr [rsp+0x20]
       vmovss   dword ptr [rsp+0x08], xmm0
       vmovss   dword ptr [rsp+0x0C], xmm1
       vmovss   dword ptr [rsp+0x10], xmm3
       vmovss   dword ptr [rsp+0x14], xmm2
       vmovsd   xmm0, qword ptr [rsp+0x08]
       vmovsd   xmm1, qword ptr [rsp+0x10]
G_M19045_IG03:  ;; offset=0x0049
       add      rsp, 40
       ret      

There are three pairs (six) of unnecessary instructions that just cancel each other, in this example. Complier should generate same code as for mutable code. Example:

    public struct StarData (float mass, float mDot, float logMsolLsol, float tSol) {
        public float mass= mass;
        public float mDot = mDot;
        public float logMsolLsol = logMsolLsol;
        public float tSol = tSol;

        public static StarData SetTsol(StarData s, float tSol){
            s.tSol=tSol;
            return s;
        }
    }

generated code for SetTsol function (https://godbolt.org/ .NET 8.0)

StarData:SetTsol(StarData,float):StarData (FullOpts):
G_M19045_IG01:  ;; offset=0x0000
       sub      rsp, 24
       vzeroupper 
       vmovsd   qword ptr [rsp+0x08], xmm0
       vmovsd   qword ptr [rsp+0x10], xmm1
G_M19045_IG02:  ;; offset=0x0013
       vmovss   dword ptr [rsp+0x14], xmm2
       vmovsd   xmm0, qword ptr [rsp+0x08]
       vmovsd   xmm1, qword ptr [rsp+0x10]
G_M19045_IG03:  ;; offset=0x0025
       add      rsp, 24
       ret      
dotnet-policy-service[bot] commented 3 months ago

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

jakobbotsch commented 3 months ago

The fundamental problem here is the same as in #11992, #89374, #91517, #93105, #96372. Related comment: https://github.com/dotnet/runtime/issues/96372#issuecomment-1875089781