3 ms·
I think that's less likely than you'd expect because the memory ordering model used by C++ and others essentially requires you to write code that works even wit
by IshKebab 8mo ago
I think that's less likely than you'd expect because the memory ordering model used by C++ and others essentially requires you to write code that works even without x86's total storage order. If you don't then you can get bugs even on x86, because the compiler will violate the ordering you thought you had in your program, even if the CPU doesn't.
Also most software runs on ARM now and I don't think that has actually happened in practice.
- rbanffy 8mo ago> Also most software runs on ARM now and I don't think that has actually happened in practice. At least in my house, ARM cores outnumber x86 cores by at least four to one. And I'm not even counting the 32-bit ARM cores in embedded devices. There is a lot of space for memory ordering bugs to manifest in all those devices.
- IshKebab 8mo agoThe number of cores is fairly similar actually. X86 has been commonly 2 or 4 core for decades, and ARM is only recently more than that.
- wat10000 8mo agoIt's definitely a real issue in real code, since the CPU isn't bound by things like function boundaries or alias analysis or pointer validity. For example: x = *a; if (x) y = *b; The compiler cannot reorder the load of b before the load of a, because it may not be a valid pointer if x is false. But the CPU is free to speculate long ahead, and if the pointer in b isn't valid, that's fine, the CPU can attempt a speculative load and fail. It's not particularly common and code that has this issue will probably crash only rarely, but it's not too hard to do.
- IshKebab 8mo agoI don't understand your example. That code is correct on both architectures.
- wat10000 8mo agoMemory ordering issues require two sides to manifest, since even the most extreme out-of-order architecture presents itself as in-order to a single thread. Imagine that code was paired with code in another thread that did: *b = newValue; barrier(); *a = 1; We put a barrier here to ensure the stores don't get reordered, since we must write out the new value in b before writing out the flag in a that tells the other code to use it. Paired with this, my example above will work fine on an architecture with strong ordering, but will potentially get a bad value into y on archs with weak ordering. The reader may fetch the value in b first, getting the value before the reassignment. It may then fetch the value in a and see 1. This will cause it to use the old value loaded from *b, which is semantically incorrect in this case even though it's perfectly correct according to the architecture and the language spec. Correct code here would need an acquire barrier on the load of a, paired with a release barrier on the store.