8 ms·
This is a little misleading. Yes, when you see something like this, changing the optimization level may indeed influence whether or not the bug is visible, and
by ohazi 13y ago
This is a little misleading.
Yes, when you see something like this, changing the optimization level may indeed influence whether or not the bug is visible, and is a useful tool for figuring out what went wrong. But these days it's pretty rare that the optimizer is at fault. Usually what you'll find is that the optimizer is indeed making a valid optimization, but your code is relying on unspecified C or C++ behavior that just happens to work when you're at -O0.
Your code is still the thing that's technically incorrect.
- derefr 13y agoIndeed, if you re-interpret the article's usage of the phrase "optimizer bug", then the advice becomes useful: if changing the optimization level fixes your broken code, you have an undefined-behavior bug.
- Eiwatah4 13y agoEither that, or a race condition.
- johnbender 13y agoI'm not sure that's necessarily the case. See my comment above about sequential consistency.
- ben0x539 13y agoAren't those undefined behavior bugs?
- Dylan16807 13y agoImagine two threads that safely lock a resource, increment it, copy the value, and then unlock it. This is entirely defined, but also a race condition. Race conditions are a lot like unsanitized input. They don't cause problems by themselves, but if you make incorrect assumptions it's easy to write incorrect code.
- gsg 13y agoIt's data races specifically that are undefined behaviour.
- johnbender 13y agoIsn't it often the case that the optimizations throw out sequential consistency guarantees? That would introduce non-determinism in concurrent executions that might have nothing to do with the semantics of the program. For example, I'm under the impression that the most recent C++ standard basically says "if you have no data races then all will be well" even though they may be benign data races like dual assignments of the same value.
- Dylan16807 13y agoC doesn't have any sequential consistency. Even without any compiler optimizations, the hardware will reorder things across cores. Subtly on x86, massively on Alpha. You can't use multiprocessing without some kind of external guarantees.
- evanphx 13y agoOrdering is basically an unspecified behavior in C. An optimizers best friend is the ability to reorder, perform CSE, etc. C and C++ are specified as the behavior of a straight-line sequence of code run in isolation. Basically, any concurrent C/C++ program treads into the unspecified behavior area. Thusly it's not the optimizers fault, but C/C++ certainly aren't helping out at all. Even the mechanisms that are common place to make a concurrent C/C++ program run correctly basically wade into unspecified behavior, for instance using asm("lock esp"), asm("mfence"), etc. TL;DR: concurrency is unspecified in C.
- ohazi 13y ago> TL;DR: concurrency is unspecified in C. One of the many reasons I'm eagerly the 1.0 release of Rust.
- swordswinger12 13y agoI think you accidentally a word.
- AaronFriel 13y agoJust a Heisenbug, he was using a browser written in C or C++.
- theseoafs 13y agoLooks like we've found a compiler bug. Who has Stallman's email?
- ohazi 13y agosigh Yeah, my edit window expired. Very eagerly awaiting. :-P
- frozenport 13y ago>TL;DR: concurrency is unspecified in C. Does the same argument apply to binary code ran on a preemptive operating systems? The actual execution order of your code is still unspecified, as the OS an interrupt it whenever it wants.
- wmil 13y agoTechnically yes, but the C++ spec gives the optimizer a lot of leeway, in ways that aren't necessarily safe. Few programmers have a deep enough understanding of the C++ spec to realize things like how the optimizer is free to ignore NULL checks after a memory location has been accessed. They just see a `pointer == NULL` passing, then code failing on a null pointer. And the problem is that they shouldn't need that level of understanding to write C++. The optimizer should be doing things like warning about unnecessary null checks that could degrade performance, it would notify the developer that there's a problem. But that would cause issues for people working with large amounts of legacy code, so it's not done.
- theseoafs 13y ago"Ignoring NULL checks after a memory location has been accessed" -- what do you mean by that?
- ben0x539 13y agoStandard example: void f(struct some_struct* p) { int x = p->some_field; /* ... */ if (p != NULL) { /* this block might be executed even if p = NULL */ } } Because reading `p->some_field` is already undefined behavior unless `p != NULL`, the compiler is free to assume that `p != NULL` is always true, and might avoid the check. If the memory access doesn't crash the program for whatever reason (maybe it got reordered somewhere else or eliminated as dead code or whatever, I dunno), then if you call that function with a NULL pointer, you fall into undefined behavior that might manifest as that check that you put right there being skipped.
- userbinator 13y agoThat would've already crashed at the p->some_field if p == NULL. > the compiler is free to assume that `p != NULL` is always true Only until the first point in the "..." part where it cannot prove that p has not been modified.
- 13y ago
- aidenn0 13y agoIn the embedded industry, the most common reason for things working when disabling optimizations is violating the ANSI aliasing rules. Embedded programmers type-pun all the time and don't realize it's undefined behavior in C. Most compilers have an option to disable the ANSI aliasing rules, but it really hurts optimization opportunities. I saw a large codebase completely break when turning on intermodule inlining. The cause: more opportunities for the compiler to find pointers that aren't allowed to alias.
- cnvogel 13y agoYes, and most often it's clearly warned about, up to the point where lines are completely burried in type-casts, just to shut up the (well meaning) compiler. Because, you know, we thrive for 0-warning compilations!
- theseoafs 13y agoThis is absolutely correct, and the article worries me for that reason. C programmers shouldn't learn "Heisenbug == compiler error" -- that's rarely the case. Compilers aren't generally formally verified, but they're also very heavily tested, and it will rarely happen that you expose a compiler bug. It's far more likely you're relying on UB in some way.
- hajile 13y agoThe author states that this has only happened 3-4 times in 30 years. I don't see anything that would make a reasonable programmer suspect that this is always the case or even usually the case.
- dave1010uk 13y agoIn this case it might be useful if compilers (or a test compiling tool) had a "go crazy for undefined behaviour option". This might be difficult to implement but could do things like randomise sort order where it's undefined. Similar to how fuzz testing an application's input, this would fuzz test the compilation phase.