开发者

Parameter passing in Visual Studio and GCC

Parameter passing in Visual Studio. Note how __m128 types are passed. Does it mean that no more than 4 __m128 arguments should be passed by value.

void good_functi开发者_运维知识库on(__m128, __m128, __m128, __m128, __m128&);
void bad_function(__m128, __m128, __m128, __m128, __m128);

Does the same rule apply to GCC?

Thank you!

EDIT: Is it possible that the fifth argument of bad_function can be misaligned? I read somewhere that only 3 arguments are passed in registers (I guess it is Win32, not x64).


The parameter passing description you linked to is actually a x86_64 Windows ABI (application binary interface) description demonstrating how values will be passed to a function at the assembly level (i.e., how the compiler will translate a C-function call to assembly). That being said, the first four arguments will be passed as pointers to the __m128 types using registers on a x86_64 platform. Since the x86_64 platform has more registers to work with compared to it's 32-bit couterpart, this type of parameter passing is done in order to speed up functions calls since accessing arguments stored in registers will be faster than accessing argument values stored in memory on the stack like you would normally see with cdecl-style function calls on a 32-bit x86 platform. If you go beyond 4 arguments, then the rest of the pointers to the __m128 type are stored on the stack. So there are no "good function" or "bad functions" simply based on the the number of arguments. In your example, both of your functions are good, it's just with the second example the rest of the arguments must use stack-space, as the number of available registers that can be used to pass values have been used up.

That being said, the pointers to the __m128 types will most likely be pointing to addresses allocated on the stack if they are automatic variables. So either way, you'll most likely be using stack space, either to store the variables being pointed to, or to pass extra arguments to functions when working with larger-than-64-bit values and other aggregate types like classes, unions, arrays, etc.

As for GCC, since what you've referenced is actually a platform-dependent ABI implementation (in this case x86_64 Windows), what you'll see on other platforms will differ somewhat, although again, for most x86_64 OS's, they will use a series of registers to pass the first couple arguments of a function call. So GCC will actually use the same rules as Visual Studio on x86_64 Windows, but on other platforms it will create different assembly based on the x86_64 ABI's for those platforms.


Argument passing is defined by a calling convention from the system ABI (Application Binary Interface). Which ABI is in use depends on what target (os + hardware platform) you're compiling for. See for example the AMD64 ABI, which *nix uses (roughly, there are a few minor variants I believe).


The link you provided was to an article on the Microsoft x64 calling convention parameter passing. It notes:

  • __m128 are always passed by pointer, not by value
  • the first four integers or pointers are always passed in RCX, RDX, R8, R9

So given any function with only __m128 arguments, up to four will pass as pointers in registers, and any others will pass as pointers on the stack. As Jason notes in another answer, these pointers will be pointing to values that are also likely on the stack.

__m128 and __m128 & (and also __m128 *) are likely equivalent in cost in the Microsoft x64 calling convention -- they all pass by pointer.


Reading through the AMD64 ABI, it looks as if the first 8 XMM registers (%xmm0 through %xmm8) are 128 bits wide and will take an __m128 by value. So on systems that use the AMD64 ABI (e.g. gcc on linux), the first eight __m128 args will end up in registers.

In this case, it might make sense to pass the 9th through 15th __m128 args as references/pointers -- they could make use of the integer registers. This would avoid copying them to stack.


I'm not sure which convention gcc on windows (e.g. mingw) uses. Presumably it must use Microsoft x64 convention if it is interacting with other libraries.


If you're curious, though, I'd highly recommend performing a few experiments and looking at the disassembly -- gcc's -S option is great for this! If you're in Visual Studio you can use the disassembly window in the debugger.

It's always good to be peaking under the hood a bit, even if you're not fully understanding what's going on. You'll start seeing patterns and can ask questions or research what you see.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜