Created
January 20, 2016 15:49
-
-
Save pilky/ebf6e8d1f0c17d9f3afd to your computer and use it in GitHub Desktop.
Compiler weirdness
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import <AppKit/AppKit.h> | |
/** | |
* This seems to work fine, if repeat count is 0 then repeatCount is set to another values. However it doesn't seem to match HUGE_VALF | |
*/ | |
void basicRepeatCount() | |
{ | |
NSInteger repeatCount = 0; | |
if (repeatCount == 0) { | |
repeatCount = HUGE_VALF; | |
} | |
NSLog(@"repeat count: %ld", (long)repeatCount); | |
} | |
/** | |
* This just prints 0. | |
From the disassembly it seems to do the following: | |
1. Put the return of -integerValue into rax | |
2. Put rax into a variable "var1" | |
3. Check if var1 == 0 | |
4. If true, put rax into var1 (again) | |
*/ | |
void repeatCountFromImageRep() | |
{ | |
NSBitmapImageRep *bitmapRep = nil; | |
NSInteger repeatCount = [[bitmapRep valueForProperty:NSImageLoopCount] integerValue]; | |
if (repeatCount == 0) { | |
repeatCount = HUGE_VALF; | |
} | |
NSLog(@"repeat count: %ld", (long)repeatCount); | |
} | |
/** | |
* This prints 0 for both statements | |
From the disassembly this seems to: | |
1. Set rcx to 0 at the start | |
2. Put the return of -integerValue into a variable (var1) | |
3. Put var1 into another variable (var2) | |
4. Check if var2 == 0 | |
5. If true, put rcx into var2 | |
*/ | |
void logInsideIfStatement() | |
{ | |
NSBitmapImageRep *bitmapRep = nil; | |
NSInteger repeatCount = [[bitmapRep valueForProperty:NSImageLoopCount] integerValue]; | |
if (repeatCount == 0) { | |
repeatCount = HUGE_VALF; | |
NSLog(@"repeat count iniside if statement: %ld", (long)repeatCount); | |
} | |
NSLog(@"repeat count: %ld", (long)repeatCount); | |
} | |
/** | |
* This prints 0 | |
The disassembly seems to be basically the same as repeatCountFromimageRep() | |
*/ | |
void repeatCountFromImageRepPropertyInVariable() | |
{ | |
NSBitmapImageRep *bitmapRep = nil; | |
NSString *property = NSImageLoopCount; | |
NSInteger repeatCount = [[bitmapRep valueForProperty:property] integerValue]; | |
if (repeatCount == 0) { | |
repeatCount = HUGE_VALF; | |
} | |
NSLog(@"repeat count: %ld", (long)repeatCount); | |
} | |
/** | |
* This prints a number for both statements, but not the one from basicRepeatCount, nor HUGE_VALF | |
The disassembly seems to imply in this case that repeatCount is set to rcx, which isn't given a value in this function | |
From the debugger though, rcx seems to be set when property is set to NSImageLoopCount | |
This somewhat matches what the project I found this bug in had in its disassembly where repeatCount was set to _NSImageLoopCount | |
*/ | |
void logInsideIfStatementPropertyInVariable() | |
{ | |
NSBitmapImageRep *bitmapRep = nil; | |
NSString *property = NSImageLoopCount; | |
NSInteger repeatCount = [[bitmapRep valueForProperty:property] integerValue]; | |
if (repeatCount == 0) { | |
repeatCount = HUGE_VALF; | |
NSLog(@"repeat count iniside if statement: %ld", (long)repeatCount); | |
} | |
NSLog(@"repeat count: %ld", (long)repeatCount); | |
} | |
/** | |
* On my machine the output is as follows | |
CompilerBug[38802:5569085] HUGE_VALF = 4328521792 | |
CompilerBug[38802:5569085] ----------basicRepeatCount()---------- | |
CompilerBug[38802:5569085] repeat count: 140735312477910 | |
CompilerBug[38802:5569085] ----------repeatCountFromImageRep()---------- | |
CompilerBug[38802:5569085] repeat count: 0 | |
CompilerBug[38802:5569085] ----------logInisideIfStatement()---------- | |
CompilerBug[38802:5569085] repeat count iniside if statement: 0 | |
CompilerBug[38802:5569085] repeat count: 0 | |
CompilerBug[38802:5569085] ----------repeatCountFromImageRepInVariable()---------- | |
CompilerBug[38802:5569085] repeat count: 0 | |
CompilerBug[38802:5569085] ----------logInsideIfStatementPropertyInVariable()---------- | |
CompilerBug[38802:5569085] repeat count iniside if statement: 140735257214216 | |
CompilerBug[38802:5569085] repeat count: 140735257214216 | |
Program ended with exit code: 0 | |
*/ | |
int main(int argc, const char * argv[]) { | |
@autoreleasepool { | |
NSLog(@"HUGE_VALF = %ld", (long)HUGE_VALF); | |
NSLog(@"----------basicRepeatCount()----------"); | |
basicRepeatCount(); | |
NSLog(@"----------repeatCountFromImageRep()----------"); | |
repeatCountFromImageRep(); | |
NSLog(@"----------logInisideIfStatement()----------"); | |
logInsideIfStatement(); | |
NSLog(@"----------repeatCountFromImageRepInVariable()----------"); | |
repeatCountFromImageRepPropertyInVariable(); | |
NSLog(@"----------logInsideIfStatementPropertyInVariable()----------"); | |
logInsideIfStatementPropertyInVariable(); | |
} | |
return 0; | |
} |
HUGE_VALF is the floating-point constant that represents "out of range" - i.e. infinity. You're attempting to convert this value to an integer. Conversion of floating point values to integers is defined as discarding the fractional part and converting the integral part to the integer type, if it is representable. It is undefined if it is not representable. The integer part of infinity doesn't really make any sense, and is not representable in integer form (ints don't have a concept of infinity) so this whole thing is Undefined Behaviour. The compiler may do what it wants with it.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
So this seems to be some sort of code weirdness I found in my code. First off, this code works fine if you use CGFloat/-floatValue rather than NSInteger/-integerValue. However, it's weird enough that I'm not sure whether it's a compiler bug that it behaves so weirdly with NSInteger, whether it's a situation where the compiler should give an error or warning, or whether it's some weirdness in C, in the C libraries or Cocoa.
I should note my assembly knowledge is limited, but I've tried to put down what the differences are from running through Hopper. Hopefully someone with more experience knows if the differences are significant or signs of what is going wrong