While trying to build ACE framework [1] using clang 3.2 with -O2 optimization I got configure script hang while checking if operator new [] throws std::bad_alloc exception (or just returns NULL).
$ cat a.cpp #include <stdexcept>
int main()
{
for (; {
try {
char *p = new char[1024];
if (p == NULL) {
return 1; // bad
}
}
catch (std::bad_alloc&) {
return 0; // good
}
}
return 0;
}
The optimizer is making not-strictly-standard assumptions about the
behavior of global operator new[] and the merits of intentionally triggering
an out-of-memory condition with a leak vs. promoting leaked allocations
to the stack. You can disable these assumptions with -fno-builtin, avoid
them by compiling at -O0, or work around them by assigning each 'p' in
turn to a volatile global variable, which will stop the compiler from realizing
that they leak.
Yes, making p to be 'static volatile char *' fixed the problem, thank you!
Do I understand you right that the compiler did the following:
1. replaced 'new char[1024]' with smth like 'alloca(1024)'
2. since alloca() cannot fail it removed try/catch and if() statements.
3. since the result isn't used 'alloca()' call was removed also.
Well for 1024 bytes I think it's reasonable, but I can see the same behavior when changing 1024 to smth bigger, like 2^30 which makes this transformation wrong from my point of view.
BTW I used to think that -O2 is more or less safe optimization level.
The optimizer is making not-strictly-standard assumptions about the
behavior of global operator new[] and the merits of intentionally triggering
an out-of-memory condition with a leak vs. promoting leaked allocations
to the stack. You can disable these assumptions with -fno-builtin, avoid
them by compiling at -O0, or work around them by assigning each 'p' in
turn to a volatile global variable, which will stop the compiler from realizing
that they leak.
Yes, making p to be 'static volatile char *' fixed the problem, thank you!
Making it "char * volatile" would be a more stable workaround; it should
prevent the optimizer from reasoning about the store at all.
Do I understand you right that the compiler did the following:
1. replaced 'new char[1024]' with smth like 'alloca(1024)'
2. since alloca() cannot fail it removed try/catch and if() statements.
3. since the result isn't used 'alloca()' call was removed also.
Something approximately like this, although I don't know the details.
Well for 1024 bytes I think it's reasonable, but I can see the same behavior when changing 1024 to smth bigger, like 2^30 which makes this transformation wrong from my point of view.
Yes, I certainly agree that arbitrary allocations should not be moved to the stack. I don't know the limits on this optimization. It may be that it wouldn't normally just turn the heap-allocation into a stack-allocation, and it's only because it's obviously completely unused that it removes the allocation entirely.
The optimizer is making not-strictly-standard assumptions about the
behavior of global operator new[] and the merits of intentionally triggering
an out-of-memory condition with a leak vs. promoting leaked allocations
to the stack. You can disable these assumptions with -fno-builtin, avoid
them by compiling at -O0, or work around them by assigning each 'p' in
turn to a volatile global variable, which will stop the compiler from realizing
that they leak.
Yes, making p to be 'static volatile char *' fixed the problem, thank you!
Do I understand you right that the compiler did the following:
1. replaced 'new char[1024]' with smth like 'alloca(1024)'
2. since alloca() cannot fail it removed try/catch and if() statements.
3. since the result isn't used 'alloca()' call was removed also.
That's not really what's happening.
The call to 'new' (or malloc or any other memory allocation function) is removed if the only uses of the return value are the following:
- comparisons with NULL
- calls to free() / delete
There is an optimization that can promote a heap allocation to a stack allocation, but only for small allocation sizes, of course.