2
votes

I'm trying to write a scheduler to run what we call "fibers". Unfortunately, I'm not really used to writing inline assembly.

typedef struct {
    //fiber's stack
    long rsp;
    long rbp;

    //next fiber in ready list
    struct fiber *next;

} fiber;

//currently executing fiber
fiber *fib;

So the very first task is - obviously - creating a fiber for the main function so it can be suspended.

int main(int argc, char* argv[]){

    //create fiber for main function
    fib = malloc(sizeof(*fib));
    __asm__(
        "movq %%rsp, %0;"
        "movq %%rbp, %1;"
         : "=r"(fib->rsp),"=r"(fib->rbp)
         );

    //jump to actual main and execute
    __asm__(...);

}

This gets compiled to

    movl    $24, %edi   #,
    call    malloc  #
#APP
# 27 "scheduler.c" 1
    movq %rsp, %rcx;movq %rbp, %rdx;    # tmp92, tmp93
# 0 "" 2
#NO_APP
    movq    %rax, fib(%rip) # tmp91, fib
    movq    %rcx, (%rax)    # tmp92, MEM[(struct fiber *)_3].rsp
    movq    %rdx, 8(%rax)   # tmp93, MEM[(struct fiber *)_3].rbp

Why does this compile movs into temporary registers? Can I somehow get rid of them?

The first version of this question had asm output from gcc -O0, with even more instructions and temporaries.

Turning on optimisations does not get rid of them.

1
Did you enable optimizations? - Jester
@Jester I hadn't at first, but turning them on does not get rid of the temporaries (see edit above). - User1291
C does not support methods - too honest for this site
Why is rbp special for your fibers? Surely you need to save/restore all registers. rsp is the only one that's special: you can use it to find a fiber's stack (where you saved the other registers, I assume). - Peter Cordes
@User1291: restore rsp, pop all the other regs one at a time, including rbp. Boom, you're back. If the function you resumed was using rbp as a frame pointer, then it still is. You've resumed from the saved context, restoring all state except for the memory where you stored registers. (Remember that the SystemV AMD64 ABI uses a 128B red-zone, so before pushing registers, you should add $-128, %rsp so you save them below the current function's red-zone. After popping the state back into regs, sub $-128, %rsp. (-128 fits in an imm8, but +128 doesn't, hence the double-negative) - Peter Cordes

1 Answers

3
votes

turning them on does not get rid of the temporaries

It did get rid of some extra loads and stores. The fib is of course still there in memory since you declared that as a global variable. The rax is the return value from the malloc that must be assigned to the fib in memory. The other two lines write into your fib members which are also required.

Since you specified register outputs the asm block can't write directly into memory. That's easy to fix with a memory constraint though:

__asm__(
    "movq %%rsp, %0;"
    "movq %%rbp, %1;"
     : "=m"(fib->rsp),"=m"(fib->rbp)
     );

This will generate:

    call    malloc
    movq    %rax, fib(%rip)
    movq    %rsp, (%rax)
    movq    %rbp, 8(%rax)