[cfe-dev] Adding support for multiple non-virtual inheritance for -cxx-abi microsoft

John McCall rjmccall at apple.com
Mon Apr 8 18:06:02 PDT 2013


On Apr 8, 2013, at 5:47 PM, Richard Smith <richard at metafoo.co.uk> wrote:
> On Mon, Apr 8, 2013 at 5:27 PM, John McCall <rjmccall at apple.com> wrote:
> On Apr 8, 2013, at 5:24 PM, Richard Smith <richard at metafoo.co.uk> wrote:
>> On Mon, Apr 8, 2013 at 11:33 AM, John McCall <rjmccall at apple.com> wrote:
>> On Apr 8, 2013, at 11:08 AM, Reid Kleckner <rnk at google.com> wrote:
>> > On Mon, Apr 8, 2013 at 2:02 PM, Timur Iskhodzhanov <timurrrr at google.com> wrote:
>> >> 2013/4/8 John McCall <rjmccall at apple.com>:
>> >>> I also find it curious that MSVC uses a thunk for member pointers, since
>> >>> the required this-adjustment is already plainly expressible in the member
>> >>> pointer value.
>> >> Me too actually.
>> >> Reid, wdyt?
>> >
>> > I think it allows them to avoid the union between non-virtual methods
>> > and virtual methods.  Seems a bit cleaner and more obvious to me, but
>> > it has tradeoffs in terms of code size at the call site and the number
>> > of conditional vs. indirect branches that you have to do:
>> > indirect to thunk and indirect through vtable, vs conditional between
>> > two indirect calls
>> 
>> Oh, does MSVC not do the union thing?  They always make a thunk to do
>> the virtual call?
>> 
>> If so, this "thunk" is potentially quite a bit more than just a thunk — it may
>> actually have ABI pointer-equality requirements on it for e.g. member
>> pointer equality tests.
>>  
>> I don't believe there's any need for that -- comparisons on pointers to virtual member functions produce unspecified results (see [expr.eq]p2, second-last sentence).
> 
> Oh, interesting.  I guess Itanium made it work because it fell out easily enough to do so.
> 
> Even on Itanium, it (arguably) doesn't always work:
> 
> struct A { virtual void f() = 0; };
> struct B { virtual void f() = 0; };
> struct C : A, B { virtual void f(); };
> void (C::*af)() = &A::f;
> void (C::*bf)() = &B::f;
> int main() { return af == bf; } // returns 0

True, although under this definition of "same member" you would presumably expect &A::f to be (indirectly) convertible to type 'void (B::*)()' and then be validly dereferenced on an object of a dynamic type that didn't include A.  We could actually make that work, but only by emitting an obscene amount of code for member conversions. :)

Also, I don't think your example needs to be even that complex;  you could probably just compare &B::f (offset sizeof(void*) when converted into C) against &C::f (offset zero).  Any sort of repeat declaration will screw it up.  But Itanium *will* succeed if you start with the address of the same *declaration*.

At any rate, it'd be interesting to know to what extent MSVC does try to make this work:  absent any conversion, do member pointers for the same virtual member fail to compare equal:
  - if produced at different times from the same expression? (pretty unlikely)
  - if produced at different times from different instantiations of the same expression? (also pretty unlikely)
  - if produced from different expressions within the same translation unit?
  - if produced from different translation units within the same linkage unit?

You could get all the way out to the last pretty easily by just emitting the thunk with a well-defined mangling and weak linkage.

John.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-dev/attachments/20130408/b0d41f5c/attachment.html>


More information about the cfe-dev mailing list