October 19, 2019
When I'm playing around with different D compilers I get different results regarding class method inlining. According most information I have seen, you must declare a class method final for the compiler to be able inline the method otherwise it will be a virtual table call.

Take this simple example.

import std.stdio;
import std.random;

class TestClass
{
    static auto rnd = Random(42);
    int tt = 3;

    void doStuff()
    {
        tt = uniform(0, 15, rnd);
    }
}

void main()
{
    auto c = new TestClass;

    c.doStuff();

    writeln("TestClass is ", c.tt);
}


The random generator is used here so that CTFE doesn't kick in.

DMD certainly doesn't inline unless I use final (on class or doStuff) but when I use LDC, LDC seems to be able to figure out that the class isn't derived and inlines it regardless (-O option required). I see that LDC generates the method doStuff so it could also be that LDC knows which specific class it is (and not a derived one) in main and can inline for this particular case. Which one is it, LDC recognizes TestClass isn't derived or is sure that the class (c) isn't derived in particular? Would that mean that the vtable entry for doStuff is created regardless? Any other information why LDC do better?

I have previously seen a failed attempt to make final default (https://forum.dlang.org/thread/lfqoan$5qq$1@digitalmars.com?page=1) but would that be necessary. Could the compiler figure out that a class method is final and manage that optimization by itself?

October 19, 2019
Just a brief answer.

On Saturday, 19 October 2019 at 15:58:08 UTC, IGotD- wrote:
> Which one is it, LDC recognizes TestClass isn't derived or is sure that the class (c) isn't derived in particular?

It is the latter: the optimizer is able to prove that object c has a vtable that is known exactly. Then indexing into that vtable gives a definite function that can then be inlined.

Some more info:
The (suboptimal) trick that LDC employs is that after the call to `new`, LDC again sets the vtable of the object. It is superfluous, because it is already done by `new`, but `new` is opaque to the optimizer whereas the extra vtable write is not. So after the object creation, the optimizer knows exactly what vtable is used for that object. Now unfortunately, that only works for the first virtual call after `new`. Any opaque function that is called with `c` as parameter, e.g. calling a virtual function of that class will destroy knowledge about what vtable is stored in `c`. Per D language spec, the virtual function cannot overwrite the vtable pointer, but the optimizer does not know that so it assumes it might be overwritten and it no longers knows the contents of the vtable pointer.
You can see this happening here:
https://d.godbolt.org/z/8ERNhg

-Johan