3 ms·
Good article, 1 comment, still. > slugging through the depths of hell where only a footnote from Raymond Chen himself will save you The behavior is well docum
by Const-me 5y ago
Good article, 1 comment, still.
> slugging through the depths of hell where only a footnote from Raymond Chen himself will save you
The behavior is well documented: https://docs.microsoft.com/en-us/cpp/build/x64-calling-convention?view=msvc-170#return-values https://docs.microsoft.com/en-us/cpp/build/x64-calling-conve...
the caller must allocate memory for the return value and pass a pointer to it as the first argument. The remaining arguments are then shifted one argument to the right. The same pointer must be returned by the callee in RAX
- Jasper_ 5y agoThis is also fairly standard as a calling convention as well? The Itanium C++ ABI (the standard for x64 on Linux) has the same text: https://itanium-cxx-abi.github.io/cxx-abi/abi.html#non-trivial-return-values https://itanium-cxx-abi.github.io/cxx-abi/abi.html#non-trivi... So either there's some extra COM consideration I'm not aware of, or there's a serious confusion in how calling conventions work. If someone returns an aggregate, what option do you have other than a pointer? If it's a callee-allocated pointer, what are the lifetime rules and how do you free it? Passing caller-allocated storage is the obvious answer and is far from unusual, or undocumented. The difference I can tell between the MSVC and Itanium C++ ABIs is just that MSVC uses "this, ret" order for methods, and Itanium uses "ret, this". There's probably some subtle differences in what constitutes "non-trivial" between the two ABIs as well.
- Const-me 5y agoApparently, the OP’s issue was ABI incompatibility between VC++ (also used by Windows SDK and everything built on top, including Direct3D 12), and System V (used on Linux, also MinGW on Windows). VC++ documentation says structures are returned through a caller-allocated pointer. Pretty sure modern VC++ optimizes that thing when it can, especially with link time code generation enabled, but for functions and COM methods imported from DLLs that’s not an option. System V http://c9x.me/compile/doc/abi.html http://c9x.me/compile/doc/abi.html only classifies values as MEMORY when they exceed 16 bytes. The D3D12_CPU_DESCRIPTOR_HANDLE only has a single size_t field, and therefore returned in RAX register in that convention. Moreover, a structure of two size_t fields would be returned in RAX + RDX registers.
- Jasper_ 5y agoSystem V is only a C ABI, not a C++ ABI, so of course it wouldn't cover C++-only details. What C++ ABI does MinGW use on Windows?
- Const-me 5y agoThat thing is COM, which is a small subset of C++ ABI. Technically it’s about the same as on Windows, i.e. C ABI with extra first argument for this pointer. Once upon a time I made this library https://github.com/const-me/comlightInterop/ https://github.com/const-me/comlightInterop/ The native side of the interop is idiomatic C++, here’s an example https://github.com/Const-me/ComLightInterop/blob/master/Demos/HelloWorldCpp/HelloWorld.cpp https://github.com/Const-me/ComLightInterop/blob/master/Demo... The C# side of the interop is implemented through the built-in C interop, here’s the relevant part of the library https://github.com/Const-me/ComLightInterop/blob/master/ComLight/Emit/NativeDelegates.cs#L39-L128 https://github.com/Const-me/ComLightInterop/blob/master/ComL... I’ve tested Linux version of that library on AMD64, ARMv7, and ARM64 CPUs, but only with gcc compiler on the native side.
- Andoryuuta 5y agoMy memory on this specific issue is a bit fuzzy, but as far as I remember the issue was regarding the definition/ABI compatibility of C++ member functions. The doc you linked aren't really clear what to do in the case of C++ member functions, as they always take a hidden "this" class instance pointer as the first argument (in MSVC). So which one is passed first when you have both? If you look at godbolt/or disassembly output of an MSVC compiled binary, you can find that it is actually passing the class pointer first, then the return value pointer. Which definitely doesn't match the absolute "the caller must allocate memory for the return value and pass a pointer to it as the first argument" statement from their own docs. Prior discussion about this at https://news.ycombinator.com/item?id=29613640 https://news.ycombinator.com/item?id=29613640
- mappu 5y agoThere is also an updated Raymond Chen footnote: https://devblogs.microsoft.com/oldnewthing/20220113-00/?p=106152 https://devblogs.microsoft.com/oldnewthing/20220113-00/?p=10...