4 ms·
> They understand that all types "are" just bytes and that all pointers "are" just register-sized integer addresses, because that's how the hardware works and h
by lelanthran 5mo ago
> They understand that all types "are" just bytes and that all pointers "are" just register-sized integer addresses, because that's how the hardware works and has worked for decades.
I'd clarify this with "They understand that all values are just bytes".
> Meanwhile, the actual computers we have been using for decades have no problems actually just loading 4 bytes through any arbitrary pointer with zero overhead.
It's partly the standards fault here - rather than saying "We don't know how vendors will implement this, so we shall leave it as implementation-defined", they say "We don't know how vendors will implement this, so we will leave it as undefined".
A clear majority of the UB problems with C could be fixed if the standards committee slowly moved all UB into IB. It's not that there isn't any progress (Signed twos-complement is coming, after all), it's that there is (I believe) much pushback from compiler authors (who dominate the standards) who don't want to make UB into IB.
- benj111 5mo ago>It's partly the standards fault here - rather than saying "We don't know how vendors will implement this, so we shall leave it as implementation-defined", they say "We don't know how vendors will implement this, so we will leave it as undefined I'd agree to a point. I still think it's unreasonable for compiler writers to get all lawyery about precise terminology. After all "implementation defined" could still be subject to the same lawyeriness (we implemented it, ergo we define it). To me this is an issue of culture. We need to push back against the view that UB means anything can happen, therefore the compiler can do anything.
- fc417fc802 5mo agoBut it's genuinely useful. In all seriousness, are you sure you aren't perhaps just using the wrong language? At this point UB and leveraging it for optimization are core parts of the most performant C implementations. That said, I think there are many cases where compilers could make a better effort to link UB they're optimizing against to UB that appears in the code as originally authored and emit a diagnostic or even error out. But at least we've got ubsan and friends so it seems like things are within reason if not optimal.
- benj111 5mo ago>are you sure you aren't perhaps just using the wrong language Well I think there is a tension here. C is the language for microcontrollers and the language for high performance. In ye olden days both groups interests were aligned because speed in C was about working with the machine. Now the UB has been highjacked for speed, that microcontroller that I'm working on, where I know and int will overflow and rely on that is UB so may be optimised out, so I then have to think about what the compiler may do. I wouldn't say C is the wrong language. I would say there are wrong compilers though.
- lelanthran 5mo ago> At this point UB and leveraging it for optimization are core parts of the most performant C implementations. I am skeptical that NULL-pointer checks being removed contribute anything more than a rounding error in performance gains in any non-trivial program.
- charleslmunger 5mo agoI got a measurable improvement from eliminating a null pointer check within the last week. Billions of devices have arm little cores, and the extra branch predictor pressure and frontend bandwidth from those instructions can be significant. A standard way to eliminate those is to invoke undefined behavior if some condition is not met; if (a == NULL) { __builtin_unreachable(); } Which then allows elimination of the null check in later code, possibly after inlining some function.
- circuit10 5mo agoThis series was a good explanation for me of why treating UB this way is genuinely useful: https://blog.llvm.org/2011/05/what-every-c-programmer-should-know.html https://blog.llvm.org/2011/05/what-every-c-programmer-should... Being able to assume certain things don't happen is powerful when you're writing optimisations, not doing that would have a real performance cost
- lelanthran 5mo ago> Being able to assume certain things don't happen is powerful when you're writing optimisations, not doing that would have a real performance cost A few of those are significant performance gains, the majority are not. Emitting the instruction for a NULL pointer dereference is effectively no more costly than not emitting that instruction. It's the code removal that's killing me.
- circuit10 5mo agoWhat if the compiler is able to use that to determine that a whole code path is dead, and then significantly improve the surrounding function because of that? Compilers optimise in multiple passes and removing things earlier can expose optimisation opportunities later that can affect other parts of the code too
- lelanthran 5mo ago> What if the compiler is able to use that to determine that a whole code path is dead, Then it should warn "unreachable code". > and then significantly improve the surrounding function because of that? It's not simply the removal that is the problem, it's that the code is silently removed.
- circuit10 5mo agoI don’t mean this in a rude way but you should really read the posts I linked, it’s interesting and part 3 especially answers these questions Direct link to part 3 (but read the others first for context if you can): https://blog.llvm.org/2011/05/what-every-c-programmer-should-know_21.html https://blog.llvm.org/2011/05/what-every-c-programmer-should... You don’t want warnings for every piece of code in a library you’re not using or sanity check you added that isn’t supposed to be hit And you can’t warn when you’re optimising based on undefined behaviour because you can’t know when it will happen, that is equivalent to the halting problem If you warned whenever undefined behaviour could be happening then e.g. every single pointer deference would say “warning: compiling assuming pointer is not null or unaligned”
- saagarjha 5mo agoTurning undefined behavior into implementation defined behavior is rarely a fix, though.
- lelanthran 5mo agoIt's a fix that removes the most pointy part of UB. "Going past the end of the array results in addressing arbitrary values" I can live with. "Going past the end of an array results in anything happening" is a hard sell.
- saagarjha 5mo agoI think it’s a really easy sell, actually: if you go past the end of the array far enough you end up accessing the stack which includes parts of the program like “where does this function return to” or “what is the index used to perform this access” or “there is no page mapped there”. None of these are arbitrary values.
- lelanthran 5mo agoThe "anything can happen" means that the compiler can simply silently refuse to emit the code does the access. Documenting that the instructions to access will always be eliminated makes it easier to predict what will happen.
- bee_rider 5mo agoCan you unravel this further (for those of us who don’t know compilers)? I’ve always assumed access past the end of an array can’t always be detected in C, so I don’t see how those instructions could be eliminated. For example, a dynamically linked library that takes in a pointer, and then writes to the 10 ints after it—whether or not this behavior is defined is determined after that library is compiled, right?
- horsawlarway 5mo ago
- mike_hock 5mo ago> A clear majority of the UB problems with C could be fixed if the standards committee slowly moved all UB into IB There is no such thing as getting rid of "all UB." What behavior is the implementation supposed to prescribe for a write to an unpredictable garbage address you read from the network? It could overwrite your code. It could overwrite any value anywhere. It could overlap with anything. Prescribing defined behavior for absolutely everything would require defining a precise, unoptimizable 1-to-1 mapping to assembly code and disallowing any multithreading.
- lelanthran 5mo ago> What behavior is the implementation supposed to prescribe for a write to an unpredictable garbage address you read from the network? I "The compiler is not allowed to elide a write to a garbage address". Wasn't that easy?
- mike_hock 5mo agoNo, because that doesn't mean anything. All the other guarantees the compiler has to make about the other parts of your program have potentially been violated by the write, so the compiler can't guarantee any particular behavior. Hence, "undefined behavior," of the entire program, not just that particular write.