5 ms·
What every compiler writer should know about programmers (2015) [pdf]
- ameliaquining 9mo agoPreviously: https://news.ycombinator.com/item?id=11219874 https://news.ycombinator.com/item?id=11219874 (2016) https://news.ycombinator.com/item?id=19659555 https://news.ycombinator.com/item?id=19659555 (2019)
- dang 9mo agoThanks! Macroexpanded: What every compiler writer should know about programmers (2015) [pdf] - https://news.ycombinator.com/item?id=19659555 https://news.ycombinator.com/item?id=19659555 - April 2019 (62 comments) What every compiler writer should know about programmers [pdf] - https://news.ycombinator.com/item?id=11219874 https://news.ycombinator.com/item?id=11219874 - March 2016 (106 comments)
- zephen 9mo agoHere's a cogent argument that any decision by compiler writers that they can do whatever they wish whenever they encounter an "undefined behavior" construct is rubbish: https://www.yodaiken.com/2021/05/19/undefined-behavior-in-c-is-a-reading-error/ https://www.yodaiken.com/2021/05/19/undefined-behavior-in-c-... And here's a cautionary tale of how a compiler writer doing whatever they wish once they encounter undefined behavior makes debugging intractable: https://www.quora.com/What-is-the-most-subtle-bug-you-have-had-to-fix-as-a-computer-programmer/answer/Tom-Lowe-34 https://www.quora.com/What-is-the-most-subtle-bug-you-have-h...
- Joker_vD 9mo agoWow, that's a very torturous reading of a specific line in a standard. And it doesn't really matter what Yodaiken thinks this line means because standard is written by C implementers for (mostly) C implementers. So if C compile writers think this line means they can use UB for optimizing purposes, then that's what it means. Yeah, I know it breaks the common illusion among the C programmers that they're "close to the bare metal", but illusions should be dispersed, not indulged. The C programmers program for the abstract C machine which is then mediated by the C compilers into machine code the way the implementers of C compilers publicly documented.
- chc4 9mo agoYeah, this is basically Sovereign Citizen-tier argumentation: through some magic of definitions and historical readings and arguing about commas, I prove that actually everyone is incorrect. That's not how programming languages work! If everyone for 10+ years has been developing compilers with some definition of undefined behavior, and all modern compilers use undefined behavior in order to drive optimization passes which depend on those invariants, there is no possible way to argue that they're wrong and you know the One True C Programming Language interpretation instead. Moreover, compiler authors don't just go out maliciously trying to ruin programs through finding more and more torturous undefined behavior for fun: the vast majority of undefined behavior in C are things that if a compiler wasn't able to assume were upheld by the programmer would inhibit trivial optimizations that the programmer also expects the compiler to be able to do.
- twoodfin 9mo agoAliasing being the classic example. If code generation for every pointer dereference has to assume that it’s potentially aliasing any other value in scope, things get slow in a hurry.
- somat 9mo agoI find where the argument gets lost is when undefined behavior is assumed to be exactly that, an invariant. That is to say, I find "could not happen" the most bizarre reading to make when optimizing around undefined behavior "whatever the machine does" makes sense, as does "we don't know". But "could not happen???" if it could not happen the spec would have said "could not happen" instead the spec does not know what will happen and so punts on the outcome, knowing full well that it will happen all the time. The problem is that there is no optimization to make around "whatever the hardware does" or "we have no clue" so the incentive is to choose the worst possible reading "undefined behavior is incorrect code and therefore a correct program will never have it".
- fluoridation 9mo agoSome behaviors are left unspecified instead of undefined, which allows each implementation to choose whatever behavior is convenient, such as, as you put it, whatever the hardware does. IIRC this is the case in C for modulo with both negative operands. I would imagine that the standard writers choose one or the other depending on whether the behavior is useful for optimizations. There's also the matter that if a behavior is currently undefined, it's easy to later on make it unspecified or specified, while if a behavior is unspecified it's more difficult to make it undefined, because you don't know how much code is depending on that behavior.
- deathanatos 9mo ago> undefined behavior makes debugging intractable: By their own admission, the compiler warns about the UB. "-Wanal"¹, as some call it, makes it an error. Under UBSan the program aborts with: code.cpp:4:6: runtime error: execution reached the end of a value-returning function without returning a value … "intractable"? ¹a humorous name for -Wextra -Wall -Werror
- zephen 9mo agoNot everybody has full control over their environment. The -Werror flag is not even religiously used for building, e.g. the linux kernel, and -Wextra can introduce a lot of extraneous garbage. This will often make it easier (though still difficult) to winnow the program down to a smaller example, as that person did, rather than to enable everything and spend weeks debugging stuff that isn't the actual problem.
- uecker 9mo agoYes, this is the funny thing. People do not want to spend time using a stricter language as already supported by C compilers using compiler flags because "it is waste of time" while others argue that we need to switch to much stricter languages. Both positions can not be true at the same time.
- Joker_vD 9mo agoIn the end it all boils to a very simple argument. The C programmers want the C compilers to behave one way, the C implementers want the C compilers to behave the other way. Since the power structure is what it is — the C implementers are the ones who write the C standard and are the ones who actually get to implement the C compilers — the C compilers do, and will, behave the way the C implementers want them to. In this situation the C programmers can either a) accept that they're programming in a language that exists as it exists, not as they'd like it to exist; b) angrily deny a); or c) switch to some other system-level language with defined semantics.
- seg_lol 9mo agoHow about we agree on the ABI and everyone can have their own C compiler. Everyone C's the world through their own lenses.
- dotancohen 9mo agoWe're not too far away from that. At the very least, Claude can provide feedback and help decide which compiler options to use, as per developer preference.
- WalterBright 9mo ago> behave the way the C implementers want them to If you don't please your users, you won't have any users.
- AlotOfReading 9mo agoAnd yet, C++.
- locknitpicker 9mo ago> And yet, C++. By any metric, C++ is one of the most successful programming languages devised by mankind, if not the most successful. What point were you trying to make?
- Animats 9mo agoNote that all the examples come from lack of bounds checking.
- Panzerschrek 9mo agoMaking C compilers better and more predictable is impossible with so many UB cases listed in the standard. A better language should be used instead, where UB and implementation-defined behavior cases are minimized.
- dotancohen 9mo agoHave you seen Rust? I'm loving it.
- uecker 9mo agoRust is not super appealing to me as C user: too complex, slow compilation, etc.
- mfru 9mo agoMaybe Zig, Hare or C3 then?
- uecker 9mo agoAlso what I like about C is that is has mature tooling, very portable with multiple implementations, and that is is very stable. I would not use a language for any serious project hat does not offer all this. Honestly, I do not think that the problem is C is o big that one needs to jump ship. There are real issues, yes, but there are also plenty of good tools and strategies to deal with UB, it is not really an issue for me.
- Panzerschrek 9mo agoZig isn't a language I mean. It's still full of footguns. It doesn't address fundamental reliability issues of C.
- mfru 9mo agoI was responding to uecker, who had other preferences
- rurban 9mo agoThis was 2015, and we still have no -Wdeadcode, warning of removal of "dead code", ie what compilers think of dead code. If a program writer writes code, it is never dead. It is written. It had purpose. If the compiler thinks this is wrong, it needs to warn about it. The only dead code is generated code by macros.
- BeeOnRope 9mo agoDead code is extremely common in C or C++ after inlining, other optimizations.
- catlifeonmars 9mo agoOP means that the code has a dual purpose: one purpose is to be compiled, the other is to communicate structure or intent to programmers.
- deathanatos 9mo agoDo we know that? I've written "dead" code. It's point was to communicate structure or intent, but it was also still dead. This pattern, in one form or another, crops up a lot IME (in multiple languages, even, with varying abilities to optimize it): if condition that is "always" false: abort with message detailing the circumstances That `if` is "dead", in the sense that the condition is always false. But "dead" sometimes is just a proof — or if I'm not rigourous enough, an assumption — in my head. If the compiler can prove the same proof I have in my head, then the dead code is eliminated. If can't, well, presumably it is left in the binary, either to never be executed, or to be executed in the case that the proof in my head is wrong.
- pmontra 9mo agoSome conditions depend strictly on inputs and the compiler can't reason much about them, and the developers can't be sure about what their users will do. So that pattern is common. It's a sibling of assertions. There are even languages with mandatory else branch.
- nananana9 9mo agoCompiler developers hijacked and twisted the term "Undefined Behavior". Everyone understood what UB was in K&R C - if you write code that the standard doesn't define a meaning to, and the compiler outputs what it outputs. If you dereference a null pointer, the compiler outputs a null pointer dereference, and when you hit it at runtime you get the undefined behavior (page fault on modern systems). Nowadays, UB means something completely different - if at any point in time, the compiler reasons out that a piece of code is only reachable via UB, it will assume that this can never happen, and will quietly delete everything downstream: https://godbolt.org/z/EYxWqcfjx https://godbolt.org/z/EYxWqcfjx
- torben-friis 9mo agoSorry if I’m missing something as this isn’t my field, but shouldn’t the two meanings be roughly equivalent to the user? As in, everything down from UB is only working by an accident of implementation that does not need to hold, and you should explicitly not rely on that. Whether the compiler happens to explicitly make it not ever work or just leaves it to fate should not be relevant.
- anilakar 9mo agoMy main gripe with UB is that if a compiler is able to detect undefined behavior invocation, it is still allowed to compile (or rather omit) said code instead of crashing.
- Doxin 9mo agoNo, because the former definition is still something you can rely on given a specific compiler and a specific machine. Hell a bunch of UB was pretty much universal anyway. Compilers would usually still emit sensible code for UB. UB just ment "the spec doesn't define what happens". It didn't use to mean "the compiler can just decide to do any wild thing if your program touches UB anywhere at anytime". Hell, with the modern definition UB can aparantly time travel. you don't even need to execute UB code for it to start doing weird shit in some cases. UB went from "whatever happens when your compiler/hardware runs this is what happens" to "Once a program contains UB the compiler doesn't need to conform to the rest of the spec anymore."
- mgaunard 9mo agoC programs with undefined behaviour were never conforming or well-working. I stopped reading at the abstract; garbage rant full of contradictions.
- moktonar 9mo agoUB is the definition of Free Will that’s why you can’t control it, and for a programmer something that cannot be controlled is felt as dangerous..
- muldvarp 9mo agoA C compiler is a relatively simple program (especially if you don't want any optimizations based on undefined behavior). If a large part of the userbase is unhappy with the way most modern C compilers work, they could easily write a "friendly"/"boring" C compiler.
- zbentley 9mo agoSome of those already exist, e.g. https://bellard.org/tcc/ https://bellard.org/tcc/ However, they're not in widespread use. I would be curious to learn if there's any data/non-anecdotal information as to why. Is it momentum/inertia of GCC/LLVM/MSVC? Are alternative compilers incomplete and can't actually compile a lot of practical programs (belying the "relatively simple program") claim? Or is the performance differential due to optimizations really so significant that ordinary programs like e.g. vim or libjpeg or VLC or whatnot have significant degradations when built on an alternative compiler?