Skip to content

Sub-optimal codegen for incrementing (some) references #12880

@kindermannhubert

Description

@kindermannhubert

Am I correct if I think the codegen for Get2 method is not ideal? Mainly I don't understand the presence of cmp instruction. And the add instruction could be reduced to movzx offset, no?

using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SharpLab.Runtime;

public static class Test
{
    public static byte Get1<T>(this T[] array)
    {
        return Unsafe.As<Exposer>(array).Byte0;
    }
    
    public static byte Get2<T>(this T[] array)
    {
        return Unsafe.AddByteOffset(ref Unsafe.As<Exposer>(array).Byte0, (IntPtr)4); //constant is arbitrary
    }
}

[StructLayout(LayoutKind.Explicit)]
internal class Exposer
{
    [FieldOffset(0)]
    public byte Byte0;
}

For x64 Core / Release:

Test.Get1[[System.Int32, System.Private.CoreLib]](Int32[])
    L0000: movzx eax, byte [rcx+0x8]
    L0004: ret

Test.Get2[[System.Int32, System.Private.CoreLib]](Int32[])
    L0000: cmp [rcx], ecx
    L0002: add rcx, 0x8
    L0006: movzx eax, byte [rcx+0x4]
    L000a: ret

https://sharplab.io/#v2:EYLgxg9gTgpgtADwGwBYA0AXEBDAzgWwB8ABAJgEYBYAKGIAYACY8gOgCUBXAOwwEt8YLAMIR8AB14AbGFADKMgG68wMXAG4a9Jq049+ggJI8ZEMfKhKV6zY1kALbFDEAZbMHbc+AjdU0BmbSQmUgYAFVUMGgBvGgY4hgBtACleDABxGC4ZZQAKDABPMRgIADMc3h4ASkqAXVj44gDmIOB8jBgGDIxyAB5QgD48u15cMISahkcobHzK+riY6njlpgB2BgBVLlxsEsEAQVwegFEEMQhcGUGpmcqWACE2mDofZYBfeYZP5NSMrKhcgUiqVylVap9GoEGK12p0YBhSH1BhhhqNQuNJlBprNPosVg11lsdnsWPsACZkx7tADyJRKlwwOVgJU2212ByOp3Olyg1yxtweTzoaAYOSMGAAChgoJUUJVXvEPtQlTQErJpRwwBhXPkIBxGTq9RgANIVMksLmSZSpcHUCrtKBcbCSYIMLkXGTRb4AMV4MEkZNp9PhOTotuWkJhHSpzx8byAA==

What I'm trying to achieve is all checks free array access. I have extension method for Span and T[]. I'm satisfied with codegen for Span variant but for array I think it could be improved.

public static class Ext
{
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static T GetDangerous<T>(this T[] array, int i)
    {
        return Unsafe.Add(
            ref Unsafe.As<byte, T>(
                ref Unsafe.AddByteOffset(
                    ref Unsafe.As<Exposer>(array).Byte0,
                    TypeData<T>.ArrayFirstItemByteOffset)),
            i);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static T GetDangerous<T>(this Span<T> span, int i)
    {
        return Unsafe.Add(ref MemoryMarshal.GetReference(span), i);
    }
}

internal class TypeData<T>
{
    public static readonly IntPtr ArrayFirstItemByteOffset = GetArrayFirstItemByteOffset();

    private static unsafe IntPtr GetArrayFirstItemByteOffset()
    {
        var array = new T[1];
        return Unsafe.ByteOffset(ref Unsafe.As<Exposer>(array).Byte0, ref Unsafe.As<T, byte>(ref array[0]));
    }
}

[StructLayout(LayoutKind.Explicit)]
internal class Exposer
{
    [FieldOffset(0)]
    public byte Byte0;
}
Ext.GetDangerous[[System.Int32, System.Private.CoreLib]](Int32[], Int32)
    L0000: cmp [rcx], ecx
    L0002: add rcx, 0x8
    L0006: movsxd rax, edx
    L0009: mov eax, [rcx+rax*4+0x8]
    L000d: ret

Ext.GetDangerous[[System.Int32, System.Private.CoreLib]](System.Span`1<Int32>, Int32)
    L0000: mov rax, [rcx]
    L0003: movsxd rdx, edx
    L0006: mov eax, [rax+rdx*4]
    L0009: ret

https://sharplab.io/#v2:EYLgxg9gTgpgtADwGwBYA0AXEBDAzgWwB8ABAJgEYBYAKGIAYACY8gOgCUBXAOwwEt8YLAMIR8AB14AbGFADKMgG68wMXAG4a9Jq049+ggJI8ZEMfKhKV6zY1kALbFDEAZbMHbc+AjdU0BmbSQmUgYAUQQMGgBvGgY4hgBtACleDABxGC4ZZQAKDABPMRgIADMc3h4ASkqAXVj4hIBZGAw7CAATA3FJHObWjq6xSQB5MT4ILlwWAEEAc1nYXFxeBRgjSQqK2dr6uOIA5iCAFQYMjAARbC5Zkw5cAB4jgD48u15cBiOEmoZHKGx8mgGBUMMDKrsGDFqPEYUwAOwMACqk2wJUE03a7RyENh8VgJSRKLRMwewHyGBgQOe2OhuLpcXxhNwqPRmIAQuSYMMSiVcC0afTBXiYATkczidMHuExBA+VAXn8AZUWByKXQ0DihbCckYMAAFDBQSpHQowS4YbAzKD/fIAMV4UFwGAMFPwqq5PL5GGqGtpQt4lR8MIAvhCIclUhkslBcgUiqVylUdn7En02p1ur0WunBiMxrwJlM5gtVMtVutNtdkzD9oFPqcWpdrrcHtTWu8GLIxFdHk8GLhu1wgSCwRCofTiAixSyZpicozmvhoPlGo5cA5JCwzmwRTJMiocgOrpVh4GIaHqBeaCCZFxsJJgp9TebsNEIQB6d/2RwuNwMdoQKoDBcBAoK4BwYgylAoKHMCXDKLw968AAXtg4xcBCtaQJMoIjtM1oAvajrOq67rcryLQMAAvAwAAcPhXtQCSyIaHBgBgrj5BAHAYDknHcRgADSFTtCw0obGAqTJjeUB3g+ZBhAgMpym+KYJPaMCSO05FejkdDVvEtZkhSDDunQDFAA=

Or is there any simpler way to achieve this?

Thanks!

category:cq
theme:basic-cq
skill-level:expert
cost:medium
impact:small

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