[il2cpp] Is `sealed` Not Worked As Said Anymore In Unity 2018.3?

In IL2CPP Optimizations: Devirtualization, it suggested use sealed keyword to devirtualization and gain performance.

But when I checked it on my own in Unity 2018.3.5, I found it did not work as the post said.

My Code is simple

public class Animal
{
    public virtual void Speak()
    {
    }
}

public class Dog : Animal
{
    public override void Speak()
    {
        Debug.Log("Dog");
    }
}

public sealed class Cat : Animal
{
    public override void Speak()
    {
        Debug.Log("Cat");
    }
}

And Test code is

Cat cat = new Cat();
cat.Speak();

Dog dog = new Dog();
dog.Speak();

And the IL Code is

    // [37 9 - 37 29]
    IL_000f: newobj       instance void Cat::.ctor()
    IL_0014: stloc.0      // cat

    // [38 9 - 38 21]
    IL_0015: ldloc.0      // cat
    IL_0016: callvirt     instance void Animal::Speak()
    IL_001b: nop

    // [40 9 - 40 29]
    IL_001c: newobj       instance void Dog::.ctor()
    IL_0021: stloc.1      // dog

    // [41 9 - 41 21]
    IL_0022: ldloc.1      // dog
    IL_0023: callvirt     instance void Animal::Speak()
    IL_0028: nop

The Cpp Code generated by IL2CPP is:

Cat_tC86B56F89EF64EBC7575B2E551959B31E9874724 * L_0 = (Cat_tC86B56F89EF64EBC7575B2E551959B31E9874724 *)il2cpp_codegen_object_new(Cat_tC86B56F89EF64EBC7575B2E551959B31E9874724_il2cpp_TypeInfo_var);
Cat__ctor_m7757DD1CFFF12E726940C571707FEBB1720A6221(L_0, /*hidden argument*/NULL);
VirtActionInvoker0::Invoke(4 /* System.Void Animal::Speak() */, L_0);
Dog_tF96DDAC719857CC7A194BF7D3A0776F8D4A6F521 * L_1 = (Dog_tF96DDAC719857CC7A194BF7D3A0776F8D4A6F521 *)il2cpp_codegen_object_new(Dog_tF96DDAC719857CC7A194BF7D3A0776F8D4A6F521_il2cpp_TypeInfo_var);
Dog__ctor_m01507A82581E1AD5704DCDA45A9DD230A5A5F764(L_1, /*hidden argument*/NULL);
VirtActionInvoker0::Invoke(4 /* System.Void Animal::Speak() */, L_1);

As you can see even I use sealed to decorate Cat, but the Cat’s Speak is still called by VirtActionInvoker0.

Do you guys meet the same problem? Or am I doing anything wrong?

You are not doing anything wrong. Unfortunately newer C# compilers emit different IL code that we used in that blog post. The different IL code uses the base class method reference for the call, which means that IL2CPP cannot assume the call can be devirtualized. We might be able to do more in-depth IL analysis of the code to guarantee devirtualization, but we’ve not implemented that yet.

1 Like

Maybe you can change the post a little bit to make it more precise. Thanks!

…It’s three years old.

Could still warrant a note to explain that it’s no longer accurate for new compilers.