IamIC
IamIC

Reputation: 18239

CIL shows `box` ops when the generic type is constrained to a class

I have the following method:

public static bool EquivalentTo<T>(this T? current, T? compare)
    where T : class
{
    if (current is null && compare is null)
        // both are null
        return true;

    if (current is null || compare is null)
        // one is null, but not both
        return false;

    return current.Equals(compare);
}

IL Spy gives the following IL in Release build:

.method public hidebysig static 
    bool EquivalentTo<class T> (
        !!T current,
        !!T compare
    ) cil managed aggressiveinlining 
{
    .custom instance void [System.Runtime]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = (
        01 00 00 00
    )
    .param type T
        .custom instance void [System.Runtime]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = (
            01 00 01 00 00
        )
    // Method begins at RVA 0x4971
    // Header size: 1
    // Code size: 54 (0x36)
    .maxstack 8

    IL_0000: ldarg.0
    IL_0001: box !!T
    IL_0006: brtrue.s IL_0012

    IL_0008: ldarg.1
    IL_0009: box !!T
    IL_000e: brtrue.s IL_0012

    IL_0010: ldc.i4.1
    IL_0011: ret

    IL_0012: ldarg.0
    IL_0013: box !!T
    IL_0018: brfalse.s IL_0022

    IL_001a: ldarg.1
    IL_001b: box !!T
    IL_0020: brtrue.s IL_0024

    IL_0022: ldc.i4.0
    IL_0023: ret

    IL_0024: ldarg.0
    IL_0025: box !!T
    IL_002a: ldarg.1
    IL_002b: box !!T
    IL_0030: callvirt instance bool [System.Runtime]System.Object::Equals(object)
    IL_0035: ret
} // end of method Extensions::EquivalentTo

I compiled with Visual Studio 2022 and .Net 8.0.100-preview.7.23376.3 in 64-bit.

I don't understand why there is boxing when the generic type is constrained to a class and thus cannot be a value type. What am I missing?

Upvotes: 1

Views: 55

Answers (1)

Charlieface
Charlieface

Reputation: 71544

box !!T br.true or br.false sequence is always a no-op for structs.

Since the JITter compiles separate code for all struct (valuetype) generic code, it can see that a box would never result in a null, so it is elided. In the case of classes (reference types) it just does a normal null check.

You can see this if you look at the JIT ASM machine code that is generated for both structs and classes.

Upvotes: 2

Related Questions