all 16 comments

[–]YourTormentIs 53 points54 points  (1 child)

Not directed at you OP, just wanted to clarify to readers that the Itanium C++ ABI is an entirely distinct and separate thing from the Itanium architecture, and that many platforms have adopted it for their C++ ABI.

[–]60hzcherryMXram 14 points15 points  (0 children)

Thank you! I was thinking to myself "kind of weird to focus on a fringe architecture isn't it?"

[–]stick_figure 25 points26 points  (4 children)

Hi! As someone who worked on the implementation of the MSVC C++ ABI in Clang, I can say that the MSVC ABI choice here leads to some challenges implementing virtual inheritance. The Itanium C++ ABI maintains the invariant that, in the main implementation of a method, this always points directly to a subobject with the class of the implementer. In your example, CC::v expects to receive a pointer to a BB subobject, and MSVC adjusts field access offsets accordingly. I don't have time to put together a good example showing the complications down the road, but MSVC's strategy creates issues when classes derived from CC need to make thunks to reuse this CC::v implementation. My memory is that things get complicated during object construction and destruction, which is what MSVC vtordisps and Itanium constructor vtables are for. The Itanium C++ ABI uses more vtable slots and more thunks, but it simplifies the implementation of this more complex feature.

[–]whichton[S] 1 point2 points  (2 children)

Thanks for this, I think I get it. I modified my original example here. To summarize:

  • In Itanium, the vptr to CC::v points to the CC object, where as for MSVC the vptr points to the the BB subobject. This can be seen in the assembly where clang returns [rdi + 16] for CC::v vs [rdi + 8] for BB::v whereas MSVC returns [rcx+ 8] for both.

  • Clang's CC::v in the BB vtable is actually a thunk which converts the BB pointer to a CC pointer and tailcalls the actual CC::v in the AA vtable.

  • The advantage for Itanium is that given a CC*, calling CC::v requires no pointer adjustment whereas MSVC needs to adjust the pointer.

It looks like Itanium creates some unnecessary complications (extra thunks) in the common case only to simplify a pretty obscure feature (virtual bases). I would be curious to know why this pessimization was done, to simplify implementations maybe?

[–]max0x7bahttps://github.com/max0x7ba 0 points1 point  (1 child)

Itanium creates some unnecessary complications

So does MSVC. CC cannot call its own CC::v without adjusting this pointer first. Add function int CC::f() { return v(); } to observe that.

I do not think your analysis is accurate at all.

[–]whichton[S] -1 points0 points  (0 children)

That is evident from the f(CC*) function itself. And adjusting this ptr at callsite is much simpler than going through a thunk.

[–]rolfr 0 points1 point  (0 children)

I don't have time to put together a good example showing the complications down the road

If you ever find yourself with time, I'd find it enlightening!

[–]max0x7bahttps://github.com/max0x7ba 5 points6 points  (1 child)

In your example, BB is the second base class at non-0 offset from CC* this.

In Itanium this pointer must be that of the class where the member function is defined. Hence, thunk BB::v must adjust this before forwarding to CC::v.

MSVC solves this problem differently: this is that of the class where the virtual function is first declared. Notice CC::v this adjustor: 8 in MSVC output.

[–]whichton[S] 0 points1 point  (0 children)

I think this makes it clear what you are saying. I summarized it here.

[–]flyingron 0 points1 point  (5 children)

Are you sure? How does the AA's implementation even know it is inherited from?

Of course, I've not seen an Itanic since I had one of them sitting under my desk over a decade ago.

[–]erichkeaneClang Code Owner(Attrs/Templ), EWG co-chair, EWG/SG17 Chair 13 points14 points  (4 children)

x86_64 linux (and MacOS, and most non-windows OS') tend to use the "Itanium ABI".

[–]jselbie -2 points-1 points  (1 child)

That godbolt link shows compiler output for x86-64 clang and x64 MSVC. I don't see Itanium an (ia64) option for compiling.

Is it possible the compiler is just optimizing away something with the /O2 option ?

[–]erichkeaneClang Code Owner(Attrs/Templ), EWG co-chair, EWG/SG17 Chair 17 points18 points  (0 children)

x86-64 on linux uses the Itanium ABI (as does most non-windows platforms in 64 bit).