I was doing some exploring around defensive copying of structs, and am trying to understand why the instructions change when passing structs of different size to a function with the in
keyword.
I have this code sample:
public readonly struct Foo1
{
public readonly int i1;
}
public readonly struct Foo2
{
public readonly int i1,i2,i3,i4,i5,i6;
}
public class C {
public static int M(Foo1 foo) {
var a = M2(foo);
return a + 1;
}
[MethodImpl(MethodImplOptions.NoInlining)]
public static int M2(in Foo1 f) =>f.i1 + 1;
[MethodImpl(MethodImplOptions.NoInlining)]
public static int M2(in Foo2 f) =>f.i1 + 1;
}
If i change M(Foo1 foo)
to M(Foo2 foo)
I get different instructions when the only difference is the size of the struct:
C.M(Foo1) :: small struct
L0000: push eax L0001: mov [esp], ecx L0004: lea ecx, [esp] L0007: call 0x376b0018 L000c: inc eax L000d: pop ecx L000e: ret
C.M(Foo2) :: large struct
L0000: lea ecx, [esp+4] L0004: call 0x376f0018 L0009: inc eax L000a: ret 0x18
Is Foo1
being passed by value here resulting in a defensive copy?
You can see the code here DEMO
Interestingly, if I remove in
for the function passing Foo1
you get less instructions to when passing Foo2
with in
:
[MethodImpl(MethodImplOptions.NoInlining)]
public static int M2(Foo1 f) =>f.i1 + 1; //`in` removed
C.M(Foo1) :: in
removed
L0000: call 0x38740018 L0005: inc eax L0006: ret
I am struggling to understand why there are differences if passing with the in
keyword. Shouldn’t it be a reference to the struct in both cases not passed by value, so why does the size of the structs change the instructions?