I added a few sentences about that to the post: > In the case of our example, the program actually compares such an “unobservable” bit pattern with a constant, so the compiler constant-folds the result to whatever it…
> Trying to figure out what a compiler might do in the face of undefined behavior is generally not a worthwhile exercise. That is exactly the point of my post! If you think I disagree with that statement, we seriously…
The argument for C not being low-level is not via UB, it is via the fact that a lot happens when C gets translated to assembly, and to explain that you need to consider an abstract machine that is many things, but not…
The standard will not specify anything, so what the compiler outputs is gibberish. You are literally looking at a sequence of bytes on which no constraints whatsoever are imposed. LLVM could have compiled my UB program…
> I wonder why the optimiser didn't choose false and let the assert pass? I don't know exactly, but it's kind of a moot point. I would have negated the statements until I found a way to make it return what I want it to.…
Another argument for considering the abstract machine as the primary way to think about programs in a language is that it is very easy to end up with a set of optimizations that all look reasonable in isolation but are…
AFAIK in Ada, deallocating memory is unsafe. So I'd say it has some catching-up to do when compared with safe Rust in that regard. And Rust of course has a two-element type, it is called `bool`. That said, Ada certainly…
Thanks, I have added a link to that LLVM document to the post!
Hm, good point about the bitfields. The paper I cite [1] actually talks specifically about bitfields as their precise semantics in the presence of "poison"-style uninitialized memory is not entirely clear yet. [1]:…
> I don't think this is really right. He claims that uninitialised memory is not just random bytes, but it is! No it's not. To describe the behavior of a program involving uninitialized memory (like the example in my…
> Unfortunately no, that's not and has never been how undefined behaviour works. Undefined behaviour anywhere in your program invalidates the whole program and can lead to arbitrary behaviour anywhere else in your…
Good point, I should have at least mentioned that there is an "abstract machine" when I introduce this strange kind of memory. Thanks for the feedback!
I don't think I got any of it wrong, but in case I did I'd appreciate if you could point out my mistake(s). :)
You are making exactly the mistake the post is all about. :) Only UB-free programs can be made sense of by looking at their assembly. Whether a program has UB is impossible to tell on that level. For that, you need to…
That and it is really tedious to spell out "less than or equal to", in German we have a much shorter phrase for it ("kleiner-gleich"). But thanks for pointing out this mistake, I will fix it immediately.
> Understanding how your compiler enforces it's abstract machine is beneficial The compiler does not enforce it though. It only implements the abstract machine, and the implementation is only correct for UB-free…
> The assembly has to enforce the abstract machine. The assembly has to implement the abstract machine only if your program has no UB. The assembly never has to check if memory is "initialized" or not even though that…
I think thinking about real hardware (most of the time) just distracts from thinking about what your program "actually does", which is specified by the abstract machine. By thinking in terms of the abstract machine, you…
IMO the real machines are much more distracting than the abstract ones. ;)
You could always say that wrap-around has to either (a) cause SIGILL or (b) return the wrapped-around result. That still allows linting with a sanitizer without involving any UB at all. This is effectively what Rust…
Which major compiler is mostly implemented by academics? Neither GCC nor LLVM, for sure. Us academics do have a lot of "fun" figuring out a way to put what the compiler developers do on solid footing [1]. But this is a…
UB has [developed a lot][1] since then. Now UB is a way for the programmer to help the compiler generate better code by providing extra information that is hard for the compiler to prove itself. I think, in general,…
> the author is trying to reconcile two fundamentally irreconcilable ways of looking at memory: on the one hand, the machine view of a single address space, and on the other, the program view of distinct variables…
This is an extremely good question, and I do not have a satisfying answer. Note that `malloc` is special in the C standard as well, and AFAIK it is not possible to implement `malloc` in standard C at all. Heck, there…
This is looking at the wrong level of abstraction though. The compiler will already optimize your code in a way that multiple uses of the same uninitialized value can produce different results. Arguing about these…
I added a few sentences about that to the post: > In the case of our example, the program actually compares such an “unobservable” bit pattern with a constant, so the compiler constant-folds the result to whatever it…
> Trying to figure out what a compiler might do in the face of undefined behavior is generally not a worthwhile exercise. That is exactly the point of my post! If you think I disagree with that statement, we seriously…
The argument for C not being low-level is not via UB, it is via the fact that a lot happens when C gets translated to assembly, and to explain that you need to consider an abstract machine that is many things, but not…
The standard will not specify anything, so what the compiler outputs is gibberish. You are literally looking at a sequence of bytes on which no constraints whatsoever are imposed. LLVM could have compiled my UB program…
> I wonder why the optimiser didn't choose false and let the assert pass? I don't know exactly, but it's kind of a moot point. I would have negated the statements until I found a way to make it return what I want it to.…
Another argument for considering the abstract machine as the primary way to think about programs in a language is that it is very easy to end up with a set of optimizations that all look reasonable in isolation but are…
AFAIK in Ada, deallocating memory is unsafe. So I'd say it has some catching-up to do when compared with safe Rust in that regard. And Rust of course has a two-element type, it is called `bool`. That said, Ada certainly…
Thanks, I have added a link to that LLVM document to the post!
Hm, good point about the bitfields. The paper I cite [1] actually talks specifically about bitfields as their precise semantics in the presence of "poison"-style uninitialized memory is not entirely clear yet. [1]:…
> I don't think this is really right. He claims that uninitialised memory is not just random bytes, but it is! No it's not. To describe the behavior of a program involving uninitialized memory (like the example in my…
> Unfortunately no, that's not and has never been how undefined behaviour works. Undefined behaviour anywhere in your program invalidates the whole program and can lead to arbitrary behaviour anywhere else in your…
Good point, I should have at least mentioned that there is an "abstract machine" when I introduce this strange kind of memory. Thanks for the feedback!
I don't think I got any of it wrong, but in case I did I'd appreciate if you could point out my mistake(s). :)
You are making exactly the mistake the post is all about. :) Only UB-free programs can be made sense of by looking at their assembly. Whether a program has UB is impossible to tell on that level. For that, you need to…
That and it is really tedious to spell out "less than or equal to", in German we have a much shorter phrase for it ("kleiner-gleich"). But thanks for pointing out this mistake, I will fix it immediately.
> Understanding how your compiler enforces it's abstract machine is beneficial The compiler does not enforce it though. It only implements the abstract machine, and the implementation is only correct for UB-free…
> The assembly has to enforce the abstract machine. The assembly has to implement the abstract machine only if your program has no UB. The assembly never has to check if memory is "initialized" or not even though that…
I think thinking about real hardware (most of the time) just distracts from thinking about what your program "actually does", which is specified by the abstract machine. By thinking in terms of the abstract machine, you…
IMO the real machines are much more distracting than the abstract ones. ;)
You could always say that wrap-around has to either (a) cause SIGILL or (b) return the wrapped-around result. That still allows linting with a sanitizer without involving any UB at all. This is effectively what Rust…
Which major compiler is mostly implemented by academics? Neither GCC nor LLVM, for sure. Us academics do have a lot of "fun" figuring out a way to put what the compiler developers do on solid footing [1]. But this is a…
UB has [developed a lot][1] since then. Now UB is a way for the programmer to help the compiler generate better code by providing extra information that is hard for the compiler to prove itself. I think, in general,…
> the author is trying to reconcile two fundamentally irreconcilable ways of looking at memory: on the one hand, the machine view of a single address space, and on the other, the program view of distinct variables…
This is an extremely good question, and I do not have a satisfying answer. Note that `malloc` is special in the C standard as well, and AFAIK it is not possible to implement `malloc` in standard C at all. Heck, there…
This is looking at the wrong level of abstraction though. The compiler will already optimize your code in a way that multiple uses of the same uninitialized value can produce different results. Arguing about these…