Skip to content

Instantly share code, notes, and snippets.

@pilky
Created January 20, 2016 15:49
Show Gist options
  • Save pilky/ebf6e8d1f0c17d9f3afd to your computer and use it in GitHub Desktop.
Save pilky/ebf6e8d1f0c17d9f3afd to your computer and use it in GitHub Desktop.
Compiler weirdness
#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;
}
@pilky
Copy link
Author

pilky commented Jan 20, 2016

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

@pmj
Copy link

pmj commented Jan 20, 2016

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