Why is -O2 or higher clang optimization violating this code?

I checked similar questions on the site, but could not find anything that matches my scenario. This is the code I'm trying to run (requires C ++ 14):

#include <iostream> #include <chrono> #include <thread> using namespace std; class countdownTimer { public: using duration_t = chrono::high_resolution_clock::duration; countdownTimer(duration_t duration) : duration{ duration }, paused{ true } {} countdownTimer(const countdownTimer&) = default; countdownTimer(countdownTimer&&) = default; countdownTimer& operator=(countdownTimer&&) = default; countdownTimer& operator=(const countdownTimer&) = default; void start() noexcept { if (started) return; startTime = chrono::high_resolution_clock::now(); endTime = startTime + duration; started = true; paused = false; } void pause() noexcept { if (paused || !started) return; pauseBegin = chrono::high_resolution_clock::now(); paused = true; } void resume() noexcept { if (!paused || !started) return; auto pauseDuration = chrono::high_resolution_clock::now() - pauseBegin; startTime += pauseDuration; endTime += pauseDuration; paused = false; } double remainingSeconds() const noexcept { auto ret = double{ 0.0 }; if (!started) ret = chrono::duration_cast<chrono::duration<double>>(duration).count(); else if (paused) ret = chrono::duration_cast<chrono::duration<double>>(duration - (pauseBegin - startTime)).count(); else ret = chrono::duration_cast<chrono::duration<double>>(duration - (chrono::high_resolution_clock::now() - startTime)).count(); return (ret < 0.0) ? 0.0 : ret; } duration_t remainingTime() const noexcept { auto ret = duration_t{ 0ms }; if (!started) ret = chrono::duration_cast<duration_t>(duration); else if (paused) ret = chrono::duration_cast<duration_t>(duration - (pauseBegin - startTime)); else ret = chrono::duration_cast<duration_t>(duration - (chrono::high_resolution_clock::now() - startTime)); return (ret < 0ms) ? 0ms : ret; } bool isPaused() const noexcept { return paused; } bool hasFinished() const noexcept { return remainingTime() == 0s; } void reset() noexcept { started = false; paused = true; } private: chrono::high_resolution_clock::time_point startTime; chrono::high_resolution_clock::time_point endTime; chrono::high_resolution_clock::time_point pauseBegin; duration_t duration; bool paused; bool started; }; int main() { countdownTimer timer(10s); timer.start(); while (!timer.hasFinished()) { cout << timer.remainingSeconds() << endl; this_thread::sleep_for(1s); } } 

This is a simple countdown timer class that I wrote for one of my projects. The client code in main() pretty straightforward, it should output a countdown from 10 to 0, and then exit the program. Without optimization or -O / -O1 it does just that:

 10 8.99495 7.98992 6.9849 5.97981 4.9748 3.96973 2.9687 1.9677 0.966752 Program ended with exit code: 0 

But if I increase the optimization to> = -O2 , the program will simply continue to output 10 and will work forever. The countdown just doesn't work; it gets stuck in the original value.

I am using the latest Xcode on OS X. clang --version says Apple LLVM version 7.3.0 (clang-703.0.31) .

The strange part is that my code does not contain any weird self-recording loops, undefined behavior or something like that, these are pretty much just standard library calls, so it’s very strange that optimization interrupts it.

Any ideas?

PS: I have not tried this on other compilers, but I am going to. I will update the question with these results.

+5
source share
2 answers

Error in your constructor:

  countdownTimer(duration_t duration) : duration{ duration }, paused{ true } {} 

You forgot to initialize started . This causes undefined behavior when calling start() .

No clang version that I have easy access to will diagnose this error, but GCC versions 5 and 6 (on Linux - I no longer have GCC on my Mac):

 $ g++ -O2 -Wall -Wextra -std=c++14 test.cc test.cc: In function 'int main()': test.cc:18:13: warning: '*((void*)& timer +33)' is used uninitialized in this function [-Wuninitialized] if (started) return; ^~~~~~~ test.cc:74:20: note: '*((void*)& timer +33)' was declared here countdownTimer timer(10s); ^~~~~ 

(My copy of Xcode seems a bit dated, with Apple LLVM version 7.0.2 (clang-700.1.81) , it does not change the behavior of the program in -O2 . Perhaps your clang will diagnose this error if you enabled warnings.)

(I sent an error report with GCC about IR-gobbledygook in diagnostics.)

+6
source

bool started not initialized. If you initialize it to false , it works with -O2 :

living example

You may find errors like this using the Undefined sanitizer behavior:

 $ g++ -std=c++14 -O2 -g -fsanitize=undefined -fno-omit-frame-pointer main.cpp && ./a.out main.cpp:18:9: runtime error: load of value 106, which is not a valid value for type 'bool' 
+9
source

All Articles