devirtualisation appears to crash clang on covariant functions on ARM

Hi,

Could someone familiar with the devirtualisation code in clang++ please have
a look at the attached code (which is derived from one of the few remaining
regression test failures on ARM), or suggest who's actively working in the
devirtualisation area please? It's involving the confluence of the following
C++ issues:

1. Replacement of virtual functions with direct calls when the compiler
knows the "actual" type of an pointer-to-object.
2. Use of C++11's final to make-apparent that a pointer must be to the
actual object type, not a parent-class.
3. C++ allows virtual functions to have covariant returns.
4. The ARM ABI specifies destructors return "this" as the final argument.
For virtual derived destructors this means it's a covariant return type.

When I run this on current trunk on x86-64 or with an x86-64 triple
argument, it's able to devirtualise the destructor call in f but not the
covariant function call in g.

On either an actual ARM or with triple armv7-unknown-linux-gnueabi argument
I get a crash due to the module verifier:

Function return type does not match operand type of return inst!
  ret %"struct.Test6::D"* %this1
%"struct.Test6::A"*Broken module found, compilation aborted!

It looks like what's happening is that it's correctly figuring out a direct
call to D's destructor, but because of the ABI this function is returning a
D* while some version of the function specification being compared against
is still saying the return type should be A*. For the general covariant
function who() in g, or if I change f to assign d to an A* temporary
variable and call the destructor on that, the ARM code doesn't actually
crash but returns code which isn't devirtualised.

So there's two questions here:

1. Does the devirtualiser work in the presence of covariant returns? If it
doesn't then it's unlikely the devirtualiser will work with destructors when
using the ARM ABI.
2. If it does work (even if covariance prevents devirtualisation), how can
it be stopped from crashing in the verifier in cases like this?

Many thanks for any assistance,
Dave

devirtualise-virtual-destructor-calls-final.cpp (713 Bytes)

It's difficult to debug what's going on because the module verifier is
running/crashing on the complete module once assembled. Digging through the
clang code without understanding the big picture, it looks like it might be
that the decision whether or not to devirtualise is made _before_ figuring
out the effects of the ABI on C++ issues, so that the derived function's
return type appears to be the same as the virtual method master signature.
So at that point a decision to devirtualise is done, which generates code
which then fails the verifier.

If I extend the test in CGExprCXX.cpp:220 from

    if (DevirtualizedMethod &&
        DevirtualizedMethod->getResultType().getCanonicalType() !=
         MD->getResultType().getCanonicalType())

to

    if ((DevirtualizedMethod &&
        DevirtualizedMethod->getResultType().getCanonicalType() !=
         MD->getResultType().getCanonicalType())
        >> (CGM.getContext().getTargetInfo().getCXXABI()==CXXABI_ARM &&
isa<CXXDestructorDecl>(MD)))

then clang runs OK on the test on both x86-64 and ARM EABI (although it
obviously fails to devirtualise the destructor in the ARM case). I'm not
sure this is the right approach: although I don't think there's any other
way for an ABI to convert a non-covariant virtual function "group" into a
covariant virtual function "group", I'm not confident in my knowledge of
C++'s nasty corner cases and platforms that conform ot part of the ABI to be
sure of this is only doing what's required.

Any insight from anyone who has a better understanding of this stuff would
be appreciated,

Cheers,
Dave