void *slp_switch(void *(*save_state)(void *, void *),
                 void *(*restore_state)(void *, void *), void *extra) {
  void *result, *garbage1, *garbage2;
  __asm__ volatile("pushq %%rbp\n"
                   "pushq %%rbx\n"
                   "pushq %%r12\n"
                   "pushq %%r13\n"
                   "pushq %%r14\n"
                   "pushq %%r15\n"
                   "movq %%rax, %%r12\n"
                   "movq %%rsi, %%r13\n"
                   "movq %%rsp, %%rdi\n"
                   "call *%%rcx\n"
                   "testq %%rax, %%rax\n"
                   "jz 0f\n"
                   "movq %%rax, %%rsp\n"
                   "movq %%r13, %%rsi\n"
                   "movq %%rax, %%rdi\n"
                   "call *%%r12\n"
                   "0:\n"
                   "popq %%r15\n"
                   "popq %%r14\n"
                   "popq %%r13\n"
                   "popq %%r12\n"
                   "popq %%rbx\n"
                   "popq %%rbp\n"
                   : "=a"(result), "=c"(garbage1), "=S"(garbage2)
                   : "a"(restore_state), "c"(save_state), "S"(extra)
                   : "memory", "rdx", "rdi", "r8", "r9", "r10", "r11", "xmm0",
                     "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7",
                     "xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13",
                     "xmm14", "xmm15");
  return result;
}


x86 assembly (clang, -O0):
<slp_switch>:
push   %rbp
mov    %rsp,%rbp
mov    %rdi,-0x8(%rbp)
mov    %rsi,-0x10(%rbp)
mov    %rdx,-0x18(%rbp)
mov    -0x10(%rbp),%rax
mov    -0x8(%rbp),%rcx
mov    -0x18(%rbp),%rsi
push   %rbp
push   %rbx
push   %r12
push   %r13
push   %r14
push   %r15
mov    %rax,%r12
mov    %rsi,%r13
mov    %rsp,%rdi
call   *%rcx
test   %rax,%rax
je     1146 <slp_switch+0x46>
mov    %rax,%rsp
mov    %r13,%rsi
mov    %rax,%rdi
call   *%r12
pop    %r15
pop    %r14
pop    %r13
pop    %r12
pop    %rbx
pop    %rbp
mov    %rax,-0x20(%rbp)
mov    %rcx,-0x28(%rbp)
mov    %rsi,-0x30(%rbp)
mov    -0x20(%rbp),%rax
pop    %rbp
ret

x86 assembly (clang, -O2):
<slp_switch>:
mov    %rsi,%rax
mov    %rdi,%rcx
mov    %rdx,%rsi
push   %rbp
push   %rbx
push   %r12
push   %r13
push   %r14
push   %r15
mov    %rax,%r12
mov    %rsi,%r13
mov    %rsp,%rdi
call   *%rcx
test   %rax,%rax
je     112f <slp_switch+0x2f>
mov    %rax,%rsp
mov    %r13,%rsi
mov    %rax,%rdi
call   *%r12
pop    %r15
pop    %r14
pop    %r13
pop    %r12
pop    %rbx
pop    %rbp
ret

LLVM IR (-O0):
define i8* @slp_switch(i8* (i8*, i8*)* %0, i8* (i8*, i8*)* %1, i8* %2) {
%4 = alloca i8* (i8*, i8*)*
%5 = alloca i8* (i8*, i8*)*
%6 = alloca i8*
%7 = alloca i8*
%8 = alloca i8*
%9 = alloca i8*
store i8* (i8*, i8*)* %0, i8* (i8*, i8*)** %4
store i8* (i8*, i8*)* %1, i8* (i8*, i8*)** %5
store i8* %2, i8** %6
%10 = load i8* (i8*, i8*)*, i8* (i8*, i8*)** %5
%11 = load i8* (i8*, i8*)*, i8* (i8*, i8*)** %4
%12 = load i8*, i8** %6
%13 = call { i8*, i8*, i8* } asm sideeffect "pushq %rbp\0Apushq %rbx\0Apushq %r12\0Apushq %r13\0Apushq %r14\0Apushq %r15\0Amovq %rax, %r12\0Amovq %rsi, %r13\0Amovq %rsp, %rdi\0Acall *%rcx\0Atestq %rax, %rax\0Ajz 0f\0Amovq %rax, %rsp\0Amovq %r13, %rsi\0Amovq %rax, %rdi\0Acall *%r12\0A0:\0Apopq %r15\0Apopq %r14\0Apopq %r13\0Apopq %r12\0Apopq %rbx\0Apopq %rbp\0A", "={ax},={cx},={si},{ax},{cx},{si},~{memory},~{rdx},~{rdi},~{r8},~{r9},~{r10},~{r11},~{xmm0},~{xmm1},~{xmm2},~{xmm3},~{xmm4},~{xmm5},~{xmm6},~{xmm7},~{xmm8},~{xmm9},~{xmm10},~{xmm11},~{xmm12},~{xmm13},~{xmm14},~{xmm15},~{dirflag},~{fpsr},~{flags}"(i8* (i8*, i8*)* %10, i8* (i8*, i8*)* %11, i8* %12)
%14 = extractvalue { i8*, i8*, i8* } %13, 0
%15 = extractvalue { i8*, i8*, i8* } %13, 1
%16 = extractvalue { i8*, i8*, i8* } %13, 2
store i8* %14, i8** %7
store i8* %15, i8** %8
store i8* %16, i8** %9
%17 = load i8*, i8** %7
ret i8* %17
}

LLVM IR (-O2):
define i8* @slp_switch(i8* (i8*, i8*)* %0, i8* (i8*, i8*)* %1, i8* %2) {
%4 = tail call { i8*, i8*, i8* } asm sideeffect "pushq %rbp\0Apushq %rbx\0Apushq %r12\0Apushq %r13\0Apushq %r14\0Apushq %r15\0Amovq %rax, %r12\0Amovq %rsi, %r13\0Amovq %rsp, %rdi\0Acall *%rcx\0Atestq %rax, %rax\0Ajz 0f\0Amovq %rax, %rsp\0Amovq %r13, %rsi\0Amovq %rax, %rdi\0Acall *%r12\0A0:\0Apopq %r15\0Apopq %r14\0Apopq %r13\0Apopq %r12\0Apopq %rbx\0Apopq %rbp\0A", "={ax},={cx},={si},{ax},{cx},{si},~{memory},~{rdx},~{rdi},~{r8},~{r9},~{r10},~{r11},~{xmm0},~{xmm1},~{xmm2},~{xmm3},~{xmm4},~{xmm5},~{xmm6},~{xmm7},~{xmm8},~{xmm9},~{xmm10},~{xmm11},~{xmm12},~{xmm13},~{xmm14},~{xmm15},~{dirflag},~{fpsr},~{flags}"(i8* (i8*, i8*)* %1, i8* (i8*, i8*)* %0, i8* %2)
%5 = extractvalue { i8*, i8*, i8* } %4, 0
ret i8* %5
}