-
Notifications
You must be signed in to change notification settings - Fork 5.2k
Closed
Labels
Milestone
Description
Consider the following code:
using System;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
interface IM<T>
{
bool UseDefaultM { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => true; }
ValueTask M(T instance) => throw new NotImplementedException("M must be implemented if UseDefaultM is false");
static ValueTask DefaultM(T instance) {
Console.WriteLine("Default Behaviour");
return default;
}
}
struct M : IM<int> {}
public static class Program
{
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
static void Main()
{
var m = new M();
if (((IM<int>)m).UseDefaultM)
{
IM<int>.DefaultM(42);
}
else
{
((IM<int>)m).M(42);
}
}
}
I was hoping to use this pattern to avoid boxing m, since I would have expected UseDefaultM
to be inlined, therefore avoiding boxing m
when it's a struct, since either the static DefaultM
method will be called, or the instance of IM
overrides the DIM for M
in which case it doesn't require boxing. However when I look at the jit asm on sharplab, it appears this isn't happening.
EDIT
Note that this does work when IM is non-generic:
using System;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
interface IM
{
bool UseDefaultM { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => true; }
ValueTask M(int instance) => throw new NotImplementedException("M must be implemented if UseDefaultM is false");
static ValueTask DefaultM(int instance) {
Console.WriteLine("Default Behaviour");
return default;
}
}
struct M : IM {}
public static class Program
{
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
static void Main()
{
var m = new M();
if (((IM)m).UseDefaultM)
{
IM.DefaultM(42);
}
else
{
((IM)m).M(42);
}
}
}
The jit works out that IM.DefaultM
is always called and removes both UseDefaultM
and ((IM)m).M(42)
from the generated asm:
Program.Main()
L0000: push ebp
L0001: mov ebp, esp
L0003: sub esp, 0x10
L0006: xor eax, eax
L0008: mov [ebp-8], eax
L000b: mov [ebp-4], eax
L000e: mov [ebp-0x10], eax
L0011: mov [ebp-0xc], eax
L0014: lea ecx, [ebp-0x10]
L0017: mov edx, 0x2a
L001c: call IM.DefaultM(Int32)
L0021: mov esp, ebp
L0023: pop ebp
L0024: ret
SingleAccretion