Hi, I was in contact with Apple Developer Technical Support, and it was suggested that I send a question to this list.
I will post the complete conversation below, but to summarize. I have a header file with this declaration: protected: bool gravityFlipped_; public: virtual bool getGravityFlipped(void) const { return gravityFlipped_; } public: virtual void setGravityFlipped(bool val) { gravityFlipped_ = val; } Everything works fine with optimization disabled -O0, but anything above that introduces issues. The getter seems to be changed in some way to the point where what it returns is garbage. Simply copying the declaration and placing it somewhere else in the header fixes the problem. Removing the "virtual" tag, or adding a log in the getter/setter also fixes the problem. I am out of ideas and any help you could provide would be great, thank you! Best, Robert Below is the complete conversation with Apple DTS MESSAGE 1: ------------------------------------------------------- Hi, I have run into a new problem that started happening once I moved from Xcode 6 to Xcode 7. I am using a framework called Cocos2d-x using C++. I am getting strange results when compiling with any code optimization above -C0. I use a macro called CC_SYNTHESIZE to simply create getters/setters for a variable. The macro is defined like this: #define CC_SYNTHESIZE(varType, varName, funName)\ protected: varType varName;\ public: virtual varType get##funName(void) const { return varName; }\ public: virtual void set##funName(varType var){ varName = var; } Here is an example of usage: CC_SYNTHESIZE(bool, gravityFlipped_, GravityFlipped); Without code optimization this works as intended, but with code optimization enabled this variable and others act erratically and does not change when the set function is called. If I replace the macro and instead write the code manually it works with code optimization enabled. protected: bool gravityFlipped_; public: virtual float getGravityFlipped(void) const { return gravityFlipped_; } public: virtual void setGravityFlipped(float val) { gravityFlipped_ = val; } Any idea what the problem could be or any steps for troubleshooting? RESPONSE 1: ------------------------------------------------------- Hello Robert, Thanks for contacting Apple Developer Technical Support (DTS). In general, DTS doesn’t answer questions regarding compiler optimizations, but before I direct to you other resources about the compiler optimizations, I wanted to mention a few things to you to investigate before concluding this is indeed an optimizer question. The code generated by the macro is not identical to the code generated by your direct implementation. Your macro produces code that is always using the bool primitive type, while your long-hand version mixes bool and float types. Is this an intentional difference? What behavior does the Xcode 8 beta produce? What do the call sites that call your getter and setter look like? The call sites could make a difference as well. It would be great if you could provide a focused Xcode sample which I can run to see the same thing you do, and make a note of Xcode version number (such as. 7.3.1) you are using to run the sample. MESSAGE 2: ------------------------------------------------------- Hi, You're right, I accidentally wrote float as the return type. After changing this to bool I get the same strange behaviour even without using the macro. I would create a test sample, but it could be tricky since the error (so far) only seems to happen in regards to two different variables in the whole game. What leads me to believe it is optimizer related is the irregular behaviour. I tried the project in Xcode 8 but got the same results. I am currently using Xcode 7.3.1. For example here is the header for the class containing graviyFlipped. class LevelSettingsObject : public cocos2d::CCNode { public: LevelSettingsObject() :startMiniMode_(false) ,bGIdx_(0) ,gIdx_(0) ,isLimited_(false) ,startDualMode_(false) ,twoPlayerMode_(false) ,gravityFlipped_(false) ,_songOffset(0) ,_fadeIn(false) ,_fadeOut(false) ,_level(NULL) ,_songChanged(false) ,_lastColorPage(0) ,_gLineIdx(0) ,_fontIdx(0) {} ~LevelSettingsObject(); static LevelSettingsObject* create(); bool init(); std::string getSaveString(); static LevelSettingsObject* objectFromString(std::string string); static LevelSettingsObject* objectFromDict(cocos2d::CCDictionary *dict); static LevelSettingsObject* objectFromMap(std::map<std::string, std::string> *map); void offsetMusic(); const char* getAudioFileName(); public: CC_SYNTHESIZE_RETAIN(GJEffectManager*, _colorManager, ColorManager); CC_SYNTHESIZE(int, startMode_, StartMode); CC_SYNTHESIZE(int, startSpeed_, StartSpeed); CC_SYNTHESIZE(bool, startMiniMode_, StartMiniMode); CC_SYNTHESIZE(bool, startDualMode_, StartDualMode); CC_SYNTHESIZE(bool, twoPlayerMode_, TwoPlayerMode); CC_SYNTHESIZE(float, _songOffset, SongOffset); CC_SYNTHESIZE(bool, _fadeIn, FadeIn); CC_SYNTHESIZE(bool, _fadeOut, FadeOut); CC_SYNTHESIZE(int, bGIdx_, BGIdx); CC_SYNTHESIZE(int, gIdx_, GIdx); CC_SYNTHESIZE(int, _fontIdx, FontIdx); CC_SYNTHESIZE(bool, isLimited_, IsLimited); CC_SYNTHESIZE(bool, gravityFlipped_, GravityFlipped); // <---- The problem CC_SYNTHESIZE(GJGameLevel*, _level, Level); CC_SYNTHESIZE(std::string, _songString, SongString); CC_SYNTHESIZE(bool, _songChanged, SongChanged); CC_SYNTHESIZE(int, _lastColorPage, LastColorPage); CC_SYNTHESIZE(int, _gLineIdx, GLineIdx); }; There are no other variables in the parent class CCNode that are named gravityFlipped, and this class is not inherited from, yet removing "virtual" from the getter/setter fixes the problem. Also, moving the line "CC_SYNTHESIZE(bool, gravityFlipped_, GravityFlipped);" from its current position to the bottom (below _gLineIdx) also fixes the problem. It just doesnt make any sense to me. The call to the getter has nothing complicated in it, just a basic check: void PlayLayer::setupLevelStart(LevelSettingsObject *settings) { if (settings->getGravityFlipped()) { player_->flipGravity(true, true); } //... } RESPONSE 2: ------------------------------------------------------- Hi Robert, Good job so far on removing a lot of possible reasons for this behavior - Xcode versions, removing the macro from the picture, and seeing how the position of the variable within the declaration affects this. Have you ruled out multiple threads calling the getter and setter? That would be the most obvious thing to create this behavior. Even if only one thread is involved, it could be that the optimization level is letting the CPU reorder the getter and setter calls. You could try logging when each function is called and seeing if the order is what you expect. I’d log a statement before the function is called, in the implementation of the getter/setter, and after the function call completes. If everything is in the order you expect, the addition of the log may have changed what the compiler does to the function. In some cases, trivial method implementations inside header files will be inlined to avoid the overhead of a function call. If that’s happening here, adding the log may change if the function is inlined, and change the behavior you see. One other thing to see what happens if you don’t let the compiler inline these functions. I’d do this without the logging. The following attribute does this: __attribute__((noinline)) as in: public: virtual bool getGravityFlipped(void) const __attribute__((noinline)) { return gravityFlipped_; } public: virtual void setGravityFlipped(bool val) __attribute__((noinline)) { gravityFlipped_ = val; } MESSAGE 3: ------------------------------------------------------- Hi, Thanks for the suggestions. I tried adding __attribute__((noinline)) to the functions but the problem still occured. I also tried adding logs in the getter/setter, but as you predicted once I did that the problem was fixed. Next I tried logging around the problem to see if I could find anything interesting, and it seems like the getter is the main culprit. I created the object, and used the setter to change the variable: LevelSettingsObject *object = LevelSettingsObject::create(); object->setGravityFlipped(true); CCLog("Gravity: %i, %i", object->getGravityFlipped(), object->gravityFlipped_); The log produced this result: "Gravity: 1034696192, 1" If I instead use: object->setGravityFlipped(false); The log produced this result: "Gravity: 1657850240, 0" I also tried logging getGravityFlipped() in a scheduled function to see if it changes, but getGravityFlipped() kept giving the same value. The value is random after each initialization, but then stays constant. So accessing the variable directly works, but for some reason the getter is optimized out? I could move things around to make this specific problem go away, but since I dont understand why it is happening it seems like too big a risk. RESPONSE 3: ------------------------------------------------------- Rob, I could move things around to make this specific problem go away, but since I dont understand why it is happening it seems like too big a risk. I agree - the position of the variable within the class definition changing the result is especially curious. At this point, the issue appears to be due to the code generated by the compiler, and beyond what DTS can assist you with. You should try sending your question and what you’ve learned so far to the Clang Front End Users mailing list, hosted by the LLVM open source project. <http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-users>
_______________________________________________ cfe-users mailing list cfe-users@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-users