2

If I am using the call instruction, via GNU's inline assembler in C++ code, to call a function I know uses the __stdcall convention, do I have to list any registers as clobbered?

I'm not finding great guidance on the internet, but it looks like %eax, %edx, and %ecx are caller-save, with the former two being reserved for the return value.

Here's my code. I want to know what I need to put after the third colon.

#include <cstdint>

namespace {

inline uint64_t invoke_stdcall(uint64_t (* stdcall_func)())
{
    unsigned long hi32, lo32;
    asm(
        "call %2"
        : "=d" (hi32), "=a" (lo32)
        : "m" (stdcall_func)
        : /* HELP! What goes here? */
    );
    return static_cast<uint64_t>(hi32) << 32 | static_cast<uint32_t>(lo32);
}

} // anonymous namespace

This message thread is the best I can find on the internet, but I haven't been able to find anything that says "this is what __stdcall assumes it can modify without saving"...

0xbe5077ed
  • 4,565
  • 6
  • 35
  • 77
  • 1
    Each platform has different constraints (eg. Windows and Linux won't have the same requirements, not to mention 32 vs 64 bits). The best resource I ever found is [Agner Fog's C++ calling conventions](http://www.agner.org/optimize/calling_conventions.pdf), which will give you the information you want and much more. – syam Jul 29 '13 at 19:26
  • 1
    Wikipedia is pretty good too: http://en.wikipedia.org/wiki/X86_calling_conventions – Mats Petersson Jul 29 '13 at 19:27
  • Thx both. @Mats, for whatever reason "designated for use within the function" in the Wiki wasn't explicit enough for me, but that must be what they're saying. – 0xbe5077ed Jul 29 '13 at 19:32
  • @syam, that manual is awesome. Looks like Chapter 6 sums it up. – 0xbe5077ed Jul 29 '13 at 19:34
  • As I've documented in the answer below, MS has pretty decent documentation for this. – Mats Petersson Jul 29 '13 at 19:41
  • the way you do this all you can invoke are functions that are _not_ taking arguments. What exactly is the reason for doing this, instead of using function attributes ? – FrankH. Jul 30 '13 at 11:16
  • @FrankH., I simplified for the purposes of illustration by removing the argument pushing code. This is a chunk of the marshalling glue which has to be able to invoke `__stdcall` functions from a different language environment. For the purposes of this question, all I needed to know was about register clobbering. – 0xbe5077ed Jul 30 '13 at 15:45

1 Answers1

4

MS does explain that EAX, EDX and ECX are "destroyed" by calls, all other registers must be preserved by callee in 32-bit code, link to MSDN docs - it doesn't matter which calling convention is used.

So, to be clear, you need to mark ecx as clobbered, since eax and edx are already being used in your inline assembler.

And for x86-64, the documentation is here, and says

The registers RBX, RBP, RDI, RSI, R12, R13, R14, and R15 are considered nonvolatile and must be saved and restored by a function that uses them.

ascpixi
  • 529
  • 4
  • 13
Mats Petersson
  • 126,704
  • 14
  • 140
  • 227
  • You also need to declare all of ST0..7, MM0..7, and XMM0..7 as clobbered (in 32-bit mode), and k0..7 if AVX-512. In 64-bit mode, also xmm8..15 (or xmm0..31 and k0..7 `#ifdef __AVX512F__`). See [Calling printf in extended inline ASM](https://stackoverflow.com/q/37502841) . If this is Windows, XMM6..15 are call-preserved, but YMM6..15 aren't and there's no way to tell GCC that only the upper 16 bytes can get wiped out. Perhaps `#ifdef __AVX__` around the xmm6..15 clobbers, since if this function's asm isn't using AVX, it won't care about YMM uppers. – Peter Cordes Mar 09 '23 at 02:15