[cfe-users] Code optimization issue Xcode 7

David Blaikie via cfe-users cfe-users at lists.llvm.org
Mon Jul 18 09:03:42 PDT 2016


It's really hard to help much without a reproduction - also, trying to
create a reduced example of the problem can often help you find the issue.
So, if you want further advice, I'd suggest starting by ripping apart (a
copy of) your program until you can't remove anything else without losing
the interesting/problematic behavior.

That said, generally behavior like this isn't a bug in the
compiler/optimizer, but a bug in the user code that's relying on Undefined
Behavior (null dereference, use after free, use after scope, array out of
bounds, etc) and the optimizer just decided to optimize/break it
differently today. So you could consider using tools around that (ASan,
UBSan, etc) to help see if there are problems like that you might address
first & see if they make the strange behavior go away.

On Fri, Jul 15, 2016 at 10:45 AM Robert Topala via cfe-users <
cfe-users at lists.llvm.org> wrote:

> 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 at lists.llvm.org
> http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-users
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-users/attachments/20160718/21be2fe8/attachment.html>


More information about the cfe-users mailing list