Which indicates that the space for "big obj" is allocated at function entry, not when it is constructed (and it never is, in this example).

01-12-2009

bboozzoo

Quote:

Originally Posted by abachler

stack variables are allocated on scope entry, which for inner loops is entry to the loop, so -

Code:

for(int x = 0;x<5;x++){
for(int y = 0;y<6;y++){
int z = 0;
}
}

y and z get allocated 5 times ( each entry of the inner loops scope), while x only gets allocated once on entry to the outer loop.

uhh write down this code and get it actually compiled, if x, y, z are not stored in registers then change your compiler (btw. the loop is pointless, so the compiler would most probably just get rid of it anyway)

01-12-2009

matsp

Quote:

Originally Posted by bboozzoo

uhh write down this code and get it actually compiled, if x, y, z are not stored in registers then change your compiler (btw. the loop is pointless, so the compiler would most probably just get rid of it anyway)

Or at the very least, just set z once outside the loop.

Now, if we were to set z to x * y, and the end-values for the two loops were variables rather than compile-time constants, then the compiler MAY decide to calcualte x * y each time in the loop.

But most likely [although there is no rule to say that a compiler MUST do it this way] the space for a variable is allocated at start of function, once and for all - no matter how many times the variable is actually "created" in a local scope. As has been "proven" above by brewbuck, of course.

--
Mats

01-12-2009

brewbuck

Quote:

Originally Posted by matsp

But most likely [although there is no rule to say that a compiler MUST do it this way] the space for a variable is allocated at start of function, once and for all - no matter how many times the variable is actually "created" in a local scope. As has been "proven" above by brewbuck, of course.

Although the compiler could allocate space only as needed I don't think it would be worth it. First, large objects should not be placed on the stack anyway since stack is a scarce resource. Second, fiddling around with the stack frame will most likely interfere with other optimizations the compiler is trying to perform (like delayed argument cleanup) and it might even lead to pipeline stalls or cache problems since the frame base pointer is being manipulated.

Even if your platform is extremely memory-limited, you (as the programmer) can break your function into multiple functions to gain more control over when stack allocation actually happens. I don't think any realistic compiler is going to bother optimizing this case.

01-12-2009

matsp

Quote:

Originally Posted by brewbuck

Although the compiler could allocate space only as needed I don't think it would be worth it. First, large objects should not be placed on the stack anyway since stack is a scarce resource. Second, fiddling around with the stack frame will most likely interfere with other optimizations the compiler is trying to perform (like delayed argument cleanup) and it might even lead to pipeline stalls or cache problems since the frame base pointer is being manipulated.

Even if your platform is extremely memory-limited, you (as the programmer) can break your function into multiple functions to gain more control over when stack allocation actually happens. I don't think any realistic compiler is going to bother optimizing this case.

I completely agree with all of the above - I was just pointing out that there is (as far as I can see) no reason for this to be true in ALL cases.

I have never seen a compiler that doesn't allocate the full space of the stack content at the very start of the function, and reclaiming the space at the end of the function - but I very much doubt that the standard REQUIRES this behaviour - it just makes the smallest code that runs the fastest, and since the space is PROBABLY needed in some cases, if you don't have a very borderline case of stack usage (that is, function F() is called from function G() only when it uses small amount of stack, and when F() is called without the interveening G() function, it uses the bigger amount of stack - but no, I don't think that's a case worth worrying about in a modern machine - it's a different story on severely limited stack-size processors, e.g. 6502 with a 256 byte stack [however, that probably use a software managed argument stack, because I'd very much doubt that even calling printf("Hello, World") could be done without using up the entire stack].

--
Mats

01-12-2009

CornedBee

Well, there are sensible functions where the compiler might want to avoid allocating everything up front, but I think this is rare enough that they don't.

Code:

void f()
{
if(something) {
g();
return;
}

huge_object ho;
...
}

01-12-2009

VirtualAce

I've never seen a compiler that didn't allocate the entire stack space at the start of the function. This makes sense in almost every case. A simple sub esp,size is all that is needed.

I really cannot think of many situations where this approach would not be optimal.

Code:

push ebp
mov ebp,esp
sub esp,needed_stack_space

It does not make sense to incrementally subtract various variable sizes from esp as the compiler encounters local variable declarations. It makes more sense for the compiler to first compute how much size is needed to accomodate all local variables and then allocate that in one operation as opposed to many. It also makes no sense for the compiler to delay the allocation of a stack variable until it reaches it as in the case of a loop.

In this case it has been my experience that total would be allocated long before the loop was ever reached in the code. Also if you notice the creation of total is essentially non-variant code that is inside of a loop and even the dumbest of compilers would optimize this out of the loop.
Also you can see that the i variable used to loop is obviously not being allocated every time through the loop which according to the late de-allocation crowd would happen. Clearly this is not happening.

I would say that the compiler in this case would do this:

Code:

push ebp
mov ebp,esp
sub esp,8
...

However if the compiler chose to use ECX for i and do a simple LOOP operation then the stack size would change to 4 bytes. Depending on the calling convention of the function you may or may not see the stack cleaned up.

However if the compiler chose to use ECX for i and do a simple LOOP operation then the stack size would change to 4 bytes. Depending on the calling convention of the function you may or may not see the stack cleaned up.

I would expect BOTH the i and total to be registers in this code. The compiler may even remove the entire loop, as all components are constants, and i & total aren't used outside the loop, so no need for a loop at all.

--
Mats

01-12-2009

VirtualAce

Ok so bad example. But I have seen MSVC 2003 produce some pretty stupid assembly for a loop as simple as this.

01-12-2009

matsp

Quote:

Originally Posted by Bubba

Ok so bad example. But I have seen MSVC 2003 produce some pretty stupid assembly for a loop as simple as this.

Sure, the compiler MAY not realize what's going on and leave parts or all of the loop "floating about". But it COULD remove it completely too - we can't say for sure either direction.

--
Mats

01-12-2009

abachler

Quote:

Originally Posted by bboozzoo

uhh write down this code and get it actually compiled, if x, y, z are not stored in registers then change your compiler (btw. the loop is pointless, so the compiler would most probably just get rid of it anyway)

the point of the code was to demonstrate the situation, not produce functional code.