Search code examples
c++compiler-constructionassemblymipscpu-registers

C++ CPU Register Usage


In C++, local variables are always allocated on the stack. The stack is a part of the allowed memory that your application can occupy. That memory is kept in your RAM (if not swapped out to disk). Now, does a C++ compiler always create assembler code that stores local variables on the stack?

Take, for example, the following simple code:

int foo( int n ) {
   return ++n;
}

In MIPS assembler code, this could look like this:

foo:
addi $v0, $a0, 1
jr $ra

As you can see, I didn't need to use the stack at all for n. Would the C++ compiler recognize that, and directly use the CPU's registers?

Edit: Wow, thanks a lot for your almost immediate and extensive answers! The function body of foo should of course be return ++n;, not return n++;. :)


Solution

  • Disclaimer: I don't know MIPS, but I do know some x86, and I think the principle should be the same..

    In the usual function call convention, the compiler will push the value of n onto the stack to pass it to the function foo. However, there is the fastcall convention that you can use to tell gcc to pass the value through the registers instead. (MSVC also has this option, but I'm not sure what its syntax is.)

    test.cpp:

    int foo1 (int n) { return ++n; }
    int foo2 (int n) __attribute__((fastcall));
    int foo2 (int n) {
        return ++n;
    }
    

    Compiling the above with g++ -O3 -fomit-frame-pointer -c test.cpp, I get for foo1:

    mov eax,DWORD PTR [esp+0x4]
    add eax,0x1
    ret
    

    As you can see, it reads in the value from the stack.

    And here's foo2:

    lea eax,[ecx+0x1]
    ret
    

    Now it takes the value directly from the register.

    Of course, if you inline the function the compiler will do a simple addition in the body of your larger function, regardless of the calling convention you specify. But when you can't get it inlined, this is going to happen.

    Disclaimer 2: I am not saying that you should continually second-guess the compiler. It probably isn't practical and necessary in most cases. But don't assume it produces perfect code.

    Edit 1: If you are talking about plain local variables (not function arguments), then yes, the compiler will allocate them in the registers or on the stack as it sees fit.

    Edit 2: It appears that calling convention is architecture-specific, and MIPS will pass the first four arguments on the stack, as Richard Pennington has stated in his answer. So in your case you don't have to specify the extra attribute (which is in fact an x86-specific attribute.)