Re: malloc was optimized out
Re: malloc was optimized out
- Subject: Re: malloc was optimized out
- From: Dmitry Markman <email@hidden>
- Date: Mon, 04 Jul 2016 16:19:19 -0400
Hi Clark
what do you mean by "Malloc effectively *never* returns NULL”
I’m looking at malloc man page (from Developer tools for Xcode 7.3.1) and I can read the following
RETURN VALUES
If successful, calloc(), malloc(), realloc(), reallocf(), and valloc() functions return a pointer to
allocated memory. If there is an error, they return a NULL pointer and set errno to ENOMEM.
anything else is “urban legends” :)))
and BTW malloc that returns NULL isn’t failing (strictly speaking) it’s just following its contract
if malloc SEGV then it would be failing.
how about C++ code:
#include <iostream>
int main(int argc, const char * argv[]) {
size_t need_size = 0x1000000000000;
unsigned char *chars = nullptr;
try {
chars = new unsigned char[need_size];
std::cout << "allocation success" << std::endl;
delete [] chars;
chars = nullptr;
} catch (std::bad_alloc &) {
std::cout << "allocation error" << std::endl;
}
return 0;
}
release build returns
allocation success
Program ended with exit code: 0
if we follow your logic than new never throws, which is clearly wrong
see C++ standard 18.6.1.1.3
Required behavior: Return a non-null pointer to suitably aligned storage (3.7.4),
or else throw a bad_- alloc exception.
This requirement is binding on a replacement version of this function.
as I can see that’s not what happening with clang++ and
standard doesn’t say anything about difference between optimized and non optimized code
(in relation to operator new at least)
thanks
dm
> On Jul 4, 2016, at 3:58 PM, Clark Cox <email@hidden> wrote:
>
>
>> On Jul 4, 2016, at 11:09, Dmitry Markman <email@hidden> wrote:
>>
>> Hi all
>>
>> in the Xcode 7.3.1 the following simple code
>>
>> #include <iostream>
>>
>> int main(int argc, const char * argv[]) {
>> size_t need_size = 0x1000000000000;
>>
>> void *data = malloc(need_size);
>>
>> if(data == NULL) {
>> std::cout << "data == NULL" << std::endl;
>> return 1;
>> } else {
>> std::cout << "data != NULL" << std::endl;
>> }
>> free(data);
>>
>> return 0;
>> }
>>
>> reports for the release build
>>
>> data != NULL
>> Program ended with exit code: 0
>
> Malloc effectively *never* returns NULL. In cases where that is known to be the case, the compiler is absolutely within it’s rights to remove the malloc call altogether and take the "!= NULL” path.
>
>> which is clearly wrong, because right answer would be
>
> It’s only wrong in the abstract sense. That is, conforming C code is not allowed to assume that malloc never returns NULL, but a platform (I.e. OS + compiler + libraries, etc) *can*.
>
>> data == NULL
>> Program ended with exit code: 1
>>
>>
>> g++ behaves correctly
>>
>> recently I found out that we reported that problem to Apple
>>
>> and we got an answer
>>
>> ""Engineering has determined that this issue behaves as intended based on the following information:
>>
>> The compiler "knows" how malloc works, and is allowed to optimize as if it never fails.
>>
>> We are now closing this bug report.”
>>
>> clearly in my situation clang knows nothing :((
>>
>> clang can’t optimize malloc here, because result of the malloc is used.
>
> No it isn’t.
> - The only way you “use” it is to check it against NULL
> - The compiler is free to optimize away the check and the entire “== NULL” branch, as it knows it will never happen
> - Once that check is removed, there are no other uses of data (aside from the initialization and the free)
>
> In other words, after that optimization, your code is functionally equivalent to:
>
> #include <iostream>
>
> int main(int argc, const char * argv[]) {
> size_t need_size = 0x1000000000000;
> void *data = malloc(need_size);
> std::cout << "data != NULL" << std::endl;
>
> free(data);
> return 0;
> }
>
> - The compiler knows that data is never used between the malloc and the free, in which case, it is free to reorder things like so:
>
> #include <iostream>
>
> int main(int argc, const char * argv[]) {
> size_t need_size = 0x1000000000000;
>
> free(malloc(need_size));
>
> std::cout << "data != NULL" << std::endl;
>
> return 0;
> }
>
> - The compiler also knows that free(malloc(…)) is a no-op (in terms of observable behavior within a standard C program), so it is free to change that to:
>
> #include <iostream>
>
> int main(int argc, const char * argv[]) {
> size_t need_size = 0x1000000000000;
>
> std::cout << "data != NULL" << std::endl;
>
> return 0;
> }
>
> - Also, size is now not used, so, after optimization, your code is functionally equivalent to:
>
> #include <iostream>
>
> int main(int argc, const char * argv[]) {
> std::cout << "data != NULL" << std::endl;
> return 0;
> }
>
>
>>
>> we have much more complex use case where
>>
>> malloc was optimized out,
>
> In your actual code, is the value of data actually used (I.e. Does something happen that requires its value other than calling free or checking against NULL)?
>
>>
>> I just simplified the code
>>
>> Note: -fno-builtin flag solve the problem
>>
>>
>> also if I use
>>
>> std::cout << "data != NULL" << data << std::endl;
>>
>> malloc wasn’t optimized
>
> That is because the actual value of data is now used (rather than just its "NULL-ness”), and the compiler can’t optimize that use away any more.
>
> --
> Clark Smith Cox III
> email@hidden
>
Dmitry Markman
_______________________________________________
Do not post admin requests to the list. They will be ignored.
Xcode-users mailing list (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden