Parameter order in callback function

Viewed 46

I was watching this C++Con talk https://www.youtube.com/watch?v=e8SyxB3_mnw&t=2933s&ab_channel=CppCon where something like the following code was given (https://godbolt.org/z/av4Tbn):

struct Handler {
  void handle(int x, int y, int z);

  static void callback0(void *instance, int x, int y, int z) {
    static_cast<Handler *>(instance)->handle(x, y, z);
  }

  static void callback1(int x, int y, int z, void *instance) {
    static_cast<Handler *>(instance)->handle(x, y, z);
  }
};

auto c0 = Handler::callback0;
auto c1 = Handler::callback1;

That code produces the following assembly:

Handler::callback0(void*, int, int, int):
        jmp     Handler::handle(int, int, int)
Handler::callback1(int, int, int, void*):
        mov     r8d, edi
        mov     rdi, rcx
        mov     ecx, edx
        mov     edx, esi
        mov     esi, r8d
        jmp     Handler::handle(int, int, int)
c1:
        .quad   Handler::callback1(int, int, int, void*)
c0:
        .quad   Handler::callback0(void*, int, int, int)

Clearly callback0 is the preferred version here, but I do not understand what's happening "under the hood" in callback1? Can someone elaborate?

1 Answers

In callback0(), the arguments are being passed in via CPU registers in the exact order that handle() is expecting them to be, where instance becomes handler's this pointer, so there is no need to rearrange any of values, hence why the jmp is used by itself.

In callback1, the arguments have to be rearranged into the correct CPU registers where handle() is expecting them, before the jmp can then be invoked.

This is related to how argument passing in various calling conventions works.

Related