<div dir="auto"><div>Hi Richard, <div dir="auto">What makes it different when we add main()? Isn't the complexity be same?</div><div dir="auto"><br></div><div dir="auto"><br></div><br><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Tue, Jul 13, 2021, 10:21 AM Richard Smith via llvm-dev <<a href="mailto:llvm-dev@lists.llvm.org">llvm-dev@lists.llvm.org</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div dir="ltr">On Sun, 11 Jul 2021 at 23:00, Santanu Das via llvm-dev <<a href="mailto:llvm-dev@lists.llvm.org" target="_blank" rel="noreferrer">llvm-dev@lists.llvm.org</a>> wrote:<br></div><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<div lang="EN-US">
<div>
<p class="MsoNormal">Hello community members,<u></u><u></u></p>
<p class="MsoNormal"><u></u> <u></u></p>
<p class="MsoNormal">I was experimenting to see whether LLVM is able to devirtualize calls via vtable. I have this particular example:<u></u><u></u></p>
<p class="MsoNormal"><u></u> <u></u></p>
<p class="MsoNormal">========<u></u><u></u></p>
<p class="MsoNormal">class Foo {<u></u><u></u></p>
<p class="MsoNormal">public:<u></u><u></u></p>
<p class="MsoNormal"> virtual int foo() const = 0;<u></u><u></u></p>
<p class="MsoNormal"> int baz() const { return foo(); }<u></u><u></u></p>
<p class="MsoNormal">};<u></u><u></u></p>
<p class="MsoNormal"><u></u> <u></u></p>
<p class="MsoNormal">class Bar : public Foo {<u></u><u></u></p>
<p class="MsoNormal">public:<u></u><u></u></p>
<p class="MsoNormal"> int foo() const override final { return 0xdeadbeef; }<u></u><u></u></p>
<p class="MsoNormal">};<u></u><u></u></p>
<p class="MsoNormal"><u></u> <u></u></p>
<p class="MsoNormal">int fred(Bar &x) {<u></u><u></u></p>
<p class="MsoNormal"> return x.baz();<u></u><u></u></p>
<p class="MsoNormal">}<u></u><u></u></p>
<p class="MsoNormal">========<u></u><u></u></p>
<p class="MsoNormal"><u></u> <u></u></p>
<p class="MsoNormal">As we can see, there is a call to Bar::foo(), via Foo::baz(), which returns a constant.<u></u><u></u></p>
<p class="MsoNormal">The Bar::foo() function has final specifier, hence this implementation cannot be overridden by any child class. In this case, the compiler should be able to call Bar::foo() directly instead of calling via vtable, and then should be able
to inline the const value.<u></u><u></u></p>
<p class="MsoNormal"><u></u> <u></u></p>
<p class="MsoNormal">When I compile with LLVM main branch, I see this piece of code being generated below. It makes a call to the function via vtable entry.<u></u><u></u></p>
<p class="MsoNormal"><u></u> <u></u></p>
<p class="MsoNormal">_Z4fredR3Bar:<u></u><u></u></p>
<p class="MsoNormal">movq (%rdi), %rax<u></u><u></u></p>
<p class="MsoNormal">jmpq *(%rax)<u></u><u></u></p>
<p class="MsoNormal"><u></u> <u></u></p>
<p class="MsoNormal"><u></u> <u></u></p>
<p class="MsoNormal">When I compile with GCC, I see that it is able to correctly identify that it should call Bar::foo() directly and successfully inlines the const value.<u></u><u></u></p>
<p class="MsoNormal"><u></u> <u></u></p>
<p class="MsoNormal">_Z4fredR3Bar:<u></u><u></u></p>
<p class="MsoNormal">movq (%rdi), %rax<u></u><u></u></p>
<p class="MsoNormal">movq (%rax), %rax<u></u><u></u></p>
<p class="MsoNormal">cmpq $_ZNK3Bar3fooEv, %rax<u></u><u></u></p>
<p class="MsoNormal">jne .L5<u></u><u></u></p>
<p class="MsoNormal">movl $-559038737, %eax<u></u><u></u></p>
<p class="MsoNormal">ret<u></u><u></u></p>
<p class="MsoNormal"><u></u> <u></u></p>
<p class="MsoNormal"><u></u> <u></u></p>
<p class="MsoNormal">Should LLVM be optimizing this call, or am I missing something?</p></div></div></blockquote><div><br></div><div>LLVM knows nothing about whether functions are "final"; most facts about the C++ type system are not representable in LLVM IR. The optimizations based on "final" are all done by Clang as part of lowering to LLVM IR. Clang would happily devirtualize a call to x.foo(), because it can locally see that would be a virtual call to a final function, so must call Bar::foo(), but in this example Clang can't do anything because it only reasons about the program before optimization, and LLVM can't do anything because the "final" information is not represented in LLVM IR.</div><div><br></div><div>The information we'd need to propagate into the optimizer here would be moderately complex: we don't know what x's vptr is at the point of the call to baz() (it could be that of Bar or of a class derived from Bar), but we do know what a certain slot in that vptr contains (or at least, we know a function that is equivalent to that slot's value -- I don't think there's a guarantee it's actually the same value). It'd probably be feasible to add support to LLVM to model that kind of thing, but it would probably require a fair bit of work.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div lang="EN-US"><div>
<p class="MsoNormal">-<u></u><u></u></p>
<p class="MsoNormal">Santanu Das<u></u><u></u></p>
</div>
</div>
_______________________________________________<br>
LLVM Developers mailing list<br>
<a href="mailto:llvm-dev@lists.llvm.org" target="_blank" rel="noreferrer">llvm-dev@lists.llvm.org</a><br>
<a href="https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev" rel="noreferrer noreferrer" target="_blank">https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev</a><br>
</blockquote></div></div>
_______________________________________________<br>
LLVM Developers mailing list<br>
<a href="mailto:llvm-dev@lists.llvm.org" target="_blank" rel="noreferrer">llvm-dev@lists.llvm.org</a><br>
<a href="https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev" rel="noreferrer noreferrer" target="_blank">https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev</a><br>
</blockquote></div></div></div>