5 ms·
Why is that a problem? (That's a serious question. I'm not suggesting that it isn't a problem. Apparently it is for you.) If I write printf("Hello, world\n"),
by _kst_ 5y ago
Why is that a problem? (That's a serious question. I'm not suggesting that it isn't a problem. Apparently it is for you.)
If I write printf("Hello, world\n"), all I care about is that those characters are written to the standard output stream when I run the program -- and that's all the language standard specifies. I rarely even look at the assembly or machine code.
As someone else mentioned, "gcc -fno-builtin" inhibits optimizations like this. Other compilers are likely to have similar options.
- matheusmoreira 5y agoBecause it's surprising and breaks our expectations. The author of this article clearly expected a call to printf to be present in the generated code. > all I care about is that those characters are written to the standard output stream when I run the program Sometimes people care about a lot more. Such as the ability to hook into a specific function or ensuring the compiler doesn't generate calls to certain functions. > that's all the language standard specifies The author clearly cared about the undefined behavior. He had a mental model of what would happen that was perfectly reasonable. It was constantly invalidated by the optimizer. > I rarely even look at the assembly or machine code. I do. It's very jarring when you write some code and the compiler deletes some of your calls and reorders the rest. It can really complicate debugging sessions. > gcc -fno-builtin Yeah, that's become a standard flag for me. Just checked the documentation and it turns out it's also implied by -ffreestanding which is a better language than hosted C anyway just because it gets rid of all the libc cruft.
- int_19h 5y agoThat's exactly why we have specifications and standards - so that you don't have to guess and build mental models on flimsy assumptions, and know exactly what to expect.
- matheusmoreira 5y agoThe standard leaves so much stuff undefined or implementation defined there's no way to fully understand what's going on anyway. The only mental model you can form is of the very limited non-existent abstract machine, anything else you have to guess or go to great pains to avoid. It just isn't very useful to think in those terms, so many intuitions just aren't possible. Turns the language into this huge minefield. I've given up on that. Standards don't compile code so they don't really matter in the end. I'm interested in what my compilers do and the code they generate. I've found I can just tell them to define the formerly undefined behavior, significantly improving the language as a result. No strict aliasing, forcing signed integers to wrap around as you'd expect them to, etc.
- int_19h 5y agoIt sounds like what you really want is a high-level portable assembler. Which, to be fair, is one of the niches that C has occupied... but I'm not convinced it's optimally designed for that in general, even leaving UB aside. But back in DOS days, there was something called Sphinx C--: https://bkhome.org/archive/goosee/cmm/c--doc.htm https://bkhome.org/archive/goosee/cmm/c--doc.htm. A modern cross-arch reincarnation of that could be interesting.
- matheusmoreira 5y ago> high-level portable assembler Yeah. More precisely, what I want is something that: 1. Gives me simple native code ELFs 2. Containing no symbols other than the functions I defined 3. That can interface directly with the Linux kernel with zero dependencies If I bend C enough it turns into something resembling that. Freestanding C, a couple flags to fix the language and the compiler's inline assembly to fulfill the 3rd requirement. I agree that it's not perfect for the role but C compilers are way too important for me to simply disregard them and look for or invent a new language. I'd be giving up too much. I wish the newer C standards took the time to define previously undefined behavior instead of adding even more cruft to the standard library. C11 is just the opposite of what I wanted.
- int_19h 5y agoReducing UB also reduces optimization opportunities, and these days, outside of embedded, C (or C++) is usually used because it's fast - there are better options if that's not a concern. So I don't think that the base C standard will ever do that. But there can be standards derived from it, which provide more rigorous guarantees at the cost of perf. Then you have embedded, where the norm is either gcc (which loves to optimize away UB), or bespoke compilers produced by the hardware manufacturer that are usually full of weird bugs in any case. Given that, is C really that important? I can see the ability to parse C headers as somewhat useful for the sake of interop, just so you could use all the libraries (and not just syscalls); but aside from that?
- _kst_ 5y agoThere is no "undefined behavior" in the sense that the C standard uses that term. The choice of which function to generate a call to in the generated assembly or machine code is not "behavior". The way I think of it is that a C program specifies run-time behavior (which consists mostly of I/O). Any generated code is just a way to achieve that. C is not some kind of assembly language. The standard says nothing about CPU instructions or registers. If you want to use C source code as a way to generate specific assembly or machine code, you'll have to go beyond what the language standard guarantees. If you need to do that, and you have an implementation that helps you with it, that's great. I wonder if there's enough demand for a language that's independent of the target processor but still guarantees that a source call to a given function actually results in a call to that function, that a "+" operator results in a single addition CPU instruction, and so forth. It's not something I'd have any use for myself, but that doesn't mean it wouldn't be valuable. C isn't that language, but something similar to C might be.