Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Over-release under objc ARC with -Os, but not with -O0

I've already filed a radar (rdar://12311693, http://openradar.appspot.com/12311693) for the following issue, but I thought I'd post here as well to see if anyone can spot a mistake in my code that could lead to the crash.

The following code sample leads to a crash due to an over-release when built with compiler optimization turned on (-Os), but does not crash when compiler optimization is turned off (-O0). The project is being built with Xcode 4.4.1 (4F1003), Apple LLVM compiler 4.0

The app crashes when num2 is over-released. Enable Zombie Objects to confirm that this is the case.

// This crashes under -Os, but not under -O0
NSNumber *num1 = @((float)arc4random() / (float)UINT32_MAX);
NSNumber *num2 = @((float)arc4random() / (float)UINT32_MAX);

NSNumber *foo1 = num1;
NSNumber *foo2 = num2;

for (NSUInteger i=0; i<2; i++) {

    NSLog(@"foo1: %p %@", foo1, foo1);
    NSLog(@"foo2: %p %@", foo2, foo2);

    // swap foo1 and foo2
    foo1 = num2;
    foo2 = num1;
}
like image 310
Andrew Hershberger Avatar asked Sep 17 '12 17:09

Andrew Hershberger


1 Answers

Compiler bug. Thanks for filing it.

num1 and num2 should guarantee the lifespan of the pointed-to instances. I suspect the optimizer is [correctly] reusing stack slots and [incorrectly] emitting a release/retain sequence that leads to the issue.


In response to stackmaster; it is a compiler bug. The whole point of ARC is to transition Objective-C to the point where the compiler can analyze the code with 100% certainty to know where to stick retains/releases such that you, the developer, don't have to. By and large, it can achieve exactly that, even in the face of threading, as long as both all code that uses the object in the MRR world is well behaved and the developer doesn't "escape" the object out of ARC control and do something naughty. Two big ifs, but a vast improvement over MRR.

Finally, if your code doesn't work when optimized (when no compiler bugs come into play) it is because your code is broken. A properly written optimizer does not break correctly written code.

While some compilers are notoriously buggy, LLVM is not one of them (I won't make claims about GCC because I haven't used it in years, but I'd bet the same could be said). An iOS or OS X system will executed millions and millions of lines of compiled with optimization enabled code on every boot and the system works quite well.

So, no, "the optimizer is turned on" is never the final resolution for a crash.


As Catfish_man noted, there are esoteric optimizer options that will produce technically incorrect code on purpose. They are not enabled by -Os or other "standard" optimizations. They are mostly centered on math operations and, thus, won't typically cause crashes as much as faster creep of error into calculations.

like image 179
bbum Avatar answered Nov 16 '22 02:11

bbum