Question

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++;. :)

Was it helpful?

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.)

OTHER TIPS

Yes. There is no rule that "variables are always allocated on the stack". The C++ standard says nothing about a stack.It doesn't assume that a stack exists, or that registers exist. It just says how the code should behave, not how it should be implemented.

The compiler only stores variables on the stack when it has to - when they have to live past a function call for example, or if you try to take the address of them.

The compiler isn't stupid. ;)

Yes, a good, optimizing C/C++ will optimize that. And even MUCH more: See here: Felix von Leitners Compiler Survey.

A normal C/C++ compiler will not put every variable on the stack anyway. The problem with your foo() function could be that the variable could get passed via the stack to the function (the ABI of your system (hardware/OS) defines that).

With C's register keyword you can give the compiler a hint that it would probably be good to store a variable in a register. Sample:

register int x = 10;

But remember: The compiler is free not to store x in a register if it wants to!

The answer is yes, maybe. It depends on the compiler, the optimization level, and the target processor.

In the case of the mips, the first four parameters, if small, are passed in registers and the return value is returned in a register. So your example has no requirement to allocate anything on the stack.

Actually, truth is stranger than fiction. In your case the parameter is returned unchanged: the value returned is that of n before the ++ operator:

foo:
    .frame  $sp,0,$ra
    .mask   0x00000000,0
    .fmask  0x00000000,0

    addu    $2, $zero, $4
    jr      $ra
    nop

Since your example foo function is an identity function (it just returns it's argument), my C++ compiler (VS 2008) completely removes this function call. If I change it to:

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

the compiler inlines this with

lea edx, [eax+1] 

Yes, The registers are used in C++. The MDR (memory data registers) contains the data being fetched and stored. For example, to retrieve the contents of cell 123, we would load the value 123 (in binary) into the MAR and perform a fetch operation. When the operation is done, a copy of the contents of cell 123 would be in the MDR. To store the value 98 into cell 4, we load a 4 into the MAR and a 98 into the MDR and perform a store. When the operation is completed the contents of cell 4 will have been set to 98, by discarding whatever was there previously. The data & address registers work with them to achieve this. In C++ too, when we initialize a var with a value or ask its value, the same phenomena Happens.

And, One More Thing, Modern Compilers also perform Register Allocation, which is kinda faster than memory allocation.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top