Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is Xcode/LLVM optimising out the variable this method returns when compiling for ARM64

This is a known bug and is has been submitted to bugreporter.apple.com under bug id #16040090

NB: This issue only reproducible on a 64-bit (A7) iOS device. If run in the simulator or on a 32-bit device it works fine.

Please note: This bug seems to be fixed in LLVM 5.1, this is the default compiler for Xcode 5.1 (currently in beta).

I have a method -pointValue (see below), it works fine when compiled with the optimisation level set to -O0, but using -Os (the default for Release builds) the compiler optimises out the line that sets the variable point. point is used as the return value so any other method calling this gets random values.

The issue can be fixed by declaring the points variable as volatile. However, why does the compiler think it can remove the line setting points? Is it something to do with how I have written the method?

- (CGPoint)pointValue
{
    ULongLong encodedPoint = [self unsignedLongLongValue];
    ULongLong *values = [self splitEncodedInteger:encodedPoint withShift:SHIFT];
    
    //volatile is required to prevent optimising out
    volatile CGPoint point = CGPointMake(values[0], values[1]);
    
    free(values); //clean up
    
    return point;
}

NB: -splitEncodedInteger:withShift: uses malloc() to create a 2 element array that is retuned and then this memory is free'd by the callee.

As requested, the code for -splitEncodedInteger:withShift:

- (ULongLong *)splitEncodedInteger:(ULongLong)encocedInteger withShift:(int)shiftValue
{
    ULongLong *splitFloats = malloc(sizeof(ULongLong) * 2);
    
    splitFloats[0] = (encocedInteger >> shiftValue);
    splitFloats[1] = encocedInteger - ((encocedInteger >> shiftValue) << shiftValue);
    
    return splitFloats;
}

Full source code of the encode categories can be found on GitHub.

Example project

As requested by several commenters I have created a simple test project. However, even after setting all the build settings to match the original project I myself cannot reproduce the issue. The bug only shows up on device.

The only thing I can see different is when the project is run with full optimisation on, LLDB can read some of the variables, and trying to print them to the console I get this:

(CGSize) size = <no location, value may have been optimized out>

This is exactly what I get with my failing project but in that instance passing around one of these "optimised out" variables just gives random data (resulting in massive views being created [or failing to be created]).

Below are screen shots of the example app running with -O0 and -Ofast respectively.

Example app running with -O0Expanded -O0

Example app running with -O0 (no optimisations), you can see info on the variables

Example app running with -OfastExpanded -Ofast

Example app running with -Ofast (full optimisations), you cannot see info on the variables

Running on device

This is the output when running ton device with optimisations off:

2014-02-10 11:36:00.771 optimisation-bug[461:60b] *** Compiling with no optmisations, the code should work as expected ***
2014-02-10 11:36:00.773 optimisation-bug[461:60b] encodedRect: 50332672
2014-02-10 11:36:00.774 optimisation-bug[461:60b] encodedSize: 45089712
2014-02-10 11:36:00.775 optimisation-bug[461:60b] encodePoint: 2621480
2014-02-10 11:36:05.629 optimisation-bug[461:60b] rect: {{0, 0}, {768, 1024}}
2014-02-10 11:36:06.099 optimisation-bug[461:60b] size: {688, 944}
2014-02-10 11:36:06.101 optimisation-bug[461:60b] point: 40.000000, 40.000000
2014-02-10 11:36:06.103 optimisation-bug[461:60b] DONE

This is the output when running on device with full optimisations:

2014-02-10 11:44:53.975 optimisation-bug[471:60b] >>> Compiling with -Os optmisations, the code should *NOT* work as expected <<<
2014-02-10 11:44:53.977 optimisation-bug[471:60b] encodedRect: 50332672
2014-02-10 11:44:53.977 optimisation-bug[471:60b] encodedSize: 45089712
2014-02-10 11:44:53.978 optimisation-bug[471:60b] encodePoint: 2621480
2014-02-10 11:44:58.176 optimisation-bug[471:60b] rect: {{1.3464973428307575e+19, 6174053600}, {1.3464973428307575e+19, 1.2554206452792682e+58}}
2014-02-10 11:44:58.178 optimisation-bug[471:60b] size: {1.3464973428307575e+19, 6174053568}
2014-02-10 11:44:58.180 optimisation-bug[471:60b] point: 13464973428307574784.000000, 6174053568.000000
2014-02-10 11:44:58.182 optimisation-bug[471:60b] DONE
like image 705
Richard Stelling Avatar asked Oct 20 '22 15:10

Richard Stelling


1 Answers

This issues seems to be a bug in the arm64 compiler; armv7(s) and x86 (Simulator) run the code without issue.

- (CGPoint)pointValue
{
    ULongLong encodedPoint = [self unsignedLongLongValue];
    ULongLong *values = [self splitEncodedInteger:encodedPoint withShift:SHIFT];

    //volatile is required to prevent optimising out
    /*volatile*/ CGPoint point = CGPointMake(values[0], values[1]);

    free(values); //clean up

    return point;
}

What seems to be happening is the free(values) line is being reordered in the optimisation phase and run before CGPointMake(...) causing pointer to return with garbage values.

Thanks to Mike Ash, CRD and Rob Napier.

like image 81
Richard Stelling Avatar answered Oct 27 '22 00:10

Richard Stelling