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 <clarkc...@gmail.com> wrote:
> 
> 
>> On Jul 4, 2016, at 11:09, Dmitry Markman <dmark...@mac.com> 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
> clarkc...@gmail.com
> 

Dmitry Markman


 _______________________________________________
Do not post admin requests to the list. They will be ignored.
Xcode-users mailing list      (Xcode-users@lists.apple.com)
Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/xcode-users/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Reply via email to