Pregunta

In Visual Studio 2013 a new calling convention _vectorcall exists. It is intended for usage with SSE data types that can be passed in SSE registers.

You can specify the calling convention of a member functions like so.

struct Vector{//a 16 byte aligned type
_m128i _vectorcall operator *(Vector a); 
};  

This works, it compiles, and the type can be passed by value despite having 16 alignment requirements.

On the other hand if I try to attach it to any constructors (which seems perfectly logical), it fails.

struct Vector
 _vectorcall Vector(SomeOtherTypeWith16Alignment a);
}; 

The compiler spits out the warning message (I have warnings as errors):

warning C4166: illegal calling convention for constructor/destructor.

Forcing me to change the code to this:

struct Vector{
   Vector(SomeOtherTypeWith16Alignment a); //fails to compile
}; 

Which also fails to compile because now SomeOtherTypeWith16Alignment can't be passed by value since _vectorcall isn't enabled on the constructor.

So I am forced to change it to this.

struct Vector{
  Vector(const SomeOtherTypeWith16Alignment& a);
};

Which compiles. But it is no longer using _vectorcall and likely won't pass the data in SSE registers as I'd prefer...

So basically, why the hell can't I specify the calling convention used by constructors?

This may be Visual C++ specific (_vectorcall certainly is). I have not tried this on other compilers--

¿Fue útil?

Solución

This is not so much an answer, but more of a collection of observations about this situation. This is indeed an intriguing question because there really seems to be something (I don't know what) that makes constructors (and destructors) fundamentally incompatible with calling conventions.

First of all, don't look for an answer in the C++ standard, because the standard does not care about calling conventions, in fact, the only mention of it in the standard is as an example of something that is out of the scope of that document (i.e., "implementation-defined"). So, the standard does not "forbid" the possibility of specifying or using different calling conventions for the constructors (or destructor).

From a little research and testing, it appears that the situation is as follows for the different main compilers (as best I could gather):

  • MSVC allows you to specify a calling convention, but ignores it (with a warning) for anything other than "stdcall", which it considers as meaning "thiscall" (stdcall-style thiscall), which is already the default anyways. Changing the default calling convention via a command-line option (/Gz for instance) does not affect non-static member functions such as constructors. In other words, there is no way to change the calling convention on constructors, whatever you try to specify, it will be ignored.
  • Both ICC and GCC ignore (with a warning) any attempt to specify a calling convention on the constructor.
  • Clang ignores (with a warning) any attempt to specify a calling convention on the constructor, unless it is "cdecl", which also the default (cdecl-style thiscall).

In other words, all compilers seem to be very stubborn about no allowing any other kind of calling convention on the constructors besides the default one. And the core question is: Why?

Here are a few thoughts on that (speculations).

First off, traditionally, calling conventions have been more about binary compatibility than about performance optimization, i.e., you specify calling conventions when linking with and calling functions from different languages or compilers (e.g., traditionally between C, C++, Pascal and Fortran). And so, this could explain why certain C++ features are excluded from that. For instance, if you are creating an interface for a C++ library (maybe calling it from a different language), then you either have to fall-back on a C API, meaning no classes, no constructors and no member functions, or you have to expose a C++ API on a platform with a standard ABI (e.g, Itanium) which fixes the calling convention. That makes specifying a calling convention for constructors completely useless from a binary compatibility point of view. That may explain why this has been "ignored" (not implemented by the compilers) as a low priority feature.

Another issue that might be involved here, for the constructors specifically, is that if you look for example at the Itanium ABI specification you can see that constructor calls are quite special. In particular, virtual inheritance requires a temporary virtual table pointer to be passed along. This may explain why it is quite a bit more difficult for compiler implementers to provide a variant of each possible calling convention for constructors. In other words, it's not as simple as just adding a "this" pointer as the first "hidden" argument and then applying a standard C calling convention (as it does with normal non-static member functions). I don't think that it would be impossible to implement those alternative calling conventions for constructors, but it would be extra trouble that compiler-vendors simply have not got around to implementing (yet). Also, given the calls to base class constructors, it may also create problems with diagnostics (verifying that base-class constructor calling conventions are compatible with the derived-class calling convention). So, this might simply be a case of "too much trouble; didn't do it". And the fact that the standard allows it, but none of the compilers do it is a strong indicator that this may be the case.

Beyond that, I cannot offer any definitive answer as to why this would be somehow impossible to achieve. The only way you could get a definitive answer is if you find a person who is directly involved in implementing one of those compilers and who has looked into trying to support different calling conventions on constructors. And note that it's possible that such a person doesn't even exist (your best bet might be to check with the LLVM/Clang developer community to see if anyone has looked into this problem).

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top