[cfe-users] Code optimization issue Xcode 7

Robert Topala via cfe-users cfe-users at lists.llvm.org
Fri Jul 15 02:46:31 PDT 2016


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>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-users/attachments/20160715/9c360996/attachment.html>


More information about the cfe-users mailing list