9 ms·
That data is bullshit. It measures -fno-strict-overflow -fno-strict-aliasing. I in fact agree flipping defaults on these UBs is reasonable, but that is very far
by sanxiyn 5y ago
That data is bullshit. It measures -fno-strict-overflow -fno-strict-aliasing. I in fact agree flipping defaults on these UBs is reasonable, but that is very far from "portable assembler" or UB-free C.
THE primary UB of C is out-of-bounds write. Any proposal for UB-free C should address how to compile out-of-bounds write.
If you insist out-of-bounds write should compile in "portable assembler" way and reliably corrupt memory instead of allowing compilers to assume it doesn't happen, compilers can't allocate local variables in register. If it's on stack, out-of-bounds write may reliably corrupt stack, so how can it be on register?
"Portable assembler" people invariably say "that's not what I meant!" when compiler people raise this issue, but the question is "then what do you mean?" And I am yet to see any good answer.
- nkurz 5y agoCould you you explain your position here more fully? I'm a C programmer mostly in the "portable assembler" camp, and I don't see why insisting on out-of-bound writes to be performed prevents compilers from keeping local variables in registers. I think the difference might be your use of the word "reliably". I don't think anyone in my camp is insisting that the results be reliable in the sense you seem to take for granted. What we want is for the compiler to attempt to do what it's instructed to do, without eliding code based on counterfactual reasoning that assumes the absence of undefined behavior. In the absence of an explicit read that is being ignored, we're generally fine with cached values of local variables being out of date. Maybe you could give an example that makes your point more clearly, and we can see if we actually disagree?
- sanxiyn 5y agoSee https://news.ycombinator.com/item?id=30024534 https://news.ycombinator.com/item?id=30024534 (by jcranmer) and https://news.ycombinator.com/item?id=30026095 https://news.ycombinator.com/item?id=30026095 (by me). Does it help?
- nkurz 5y agoUnfortunately, these don't help much. I thought about responding to jcranmer's post instead of yours, but wasn't sure how to approach it. It's a good comment, and I appreciated his attempt, but I feel like he's thoroughly missing the point of the complaints about UB. The complaint isn't that too much UB exists in C, but that compiler writers seem to use the presence of UB as an excuse for being allowed to break code that has worked historically. And the complaint isn't just that the code is broken, but that no apparent care is taken to avoid doing so despite the small amount of gain. It's a worldview mismatch, and I don't know how to bridge it. Your comment seems about the same as the one I responded to. You seem to assume that people who complain about UB in C would have a problem with keeping local variables in registers, but I've never seen anyone actually make this complaint. Take for example the Arxiv paper we are discussing: he doesn't bring this up. This makes me suspect that your mental model of the people who are complaining about UB in C is probably flawed. I understand the technical issue you are gesturing toward, I just don't see it as being something that refutes any of the issues brought up in the Arxiv paper. My hope was that a concrete example might help to clarify, but I do realize this might not be the right forum for that.
- sanxiyn 5y agoNo, I don't assume people are against register allocation, but any concrete proposal I have seen kind of implies such conclusion. I am trying to understand what people actually want, since they seem clearly different from what people say they want. Okay let's discuss a concrete example. *x = 12345678; f(); return *x; // can you copy propagate 12345678 to here? f() does this: for (int *p = 0; p++; p < MEMSIZE) if (*p == 12345678) *p = 12345679. That is, f scans the memory for 12345678 and replace all instances with 12345679. There is no doubt this actually works that way in assembly. Things like cheat engines do this! C compilers assume this doesn't happen, because it is UB. Hence, portable assembly C compiler can't omit any load. Now I understand there are minority of people who will answer "that's what I want!", but like register allocation, I think people generally want this to optimize. But that necessarily implies memory search-and-replace can't compile in portable assembly manner.
- ghoward 5y agoI want a safe C. If that means I have to deal with fewer available optimizations, I'm all for that! That's why I wrote two's-complement arithmetic with unsigned types and why I actually have bounds checks on my array accesses in my code. That means I would gladly take a compiler that could not allocate local variables in registers. Or even better, just do a bounds check. The standard does not preclude storing a raw pointer and a length together. Why couldn't the compiler do something like that and do a bounds check? (Yes, the compiler would have to use its own version of libc to make that happen, but it could.) In other words, I want correct code first, performant code second. I'll take the performance hit for correct code. People claim that exploiting UB gives 1-2% speedup and that that's important. What they conveniently leave out is that you only get that speedup when your code has UB, which means that you only get that speedup when your code has a bug in it. Fast wrong code is bad. Very bad. Correct code, even if slower, is an absolute must.
- sanxiyn 5y agoYes, this is a consistent position, but bounds checking all indexing and making pointers larger by including length will cause slowdown much larger than 1-2%. My estimate is at least 10% slowdown. So citing 1-2% slowdown is misleading.
- ghoward 5y agoI agree 1-2% might be misleading, but Dr. John Regehr puts overflow checking overhead at 5%. [1] I'll take 5% or even 10% for correctness because, again, fast and wrong is very bad. [1]: https://blog.regehr.org/archives/1154 https://blog.regehr.org/archives/1154
- Joky 5y agoYou may be interested by https://www.microsoft.com/en-us/research/project/checked-c/ https://www.microsoft.com/en-us/research/project/checked-c/
- foxfluff 5y ago> People claim that exploiting UB gives 1-2% speedup and that that's important. What they conveniently leave out is that you only get that speedup when your code has UB, which means that you only get that speedup when your code has a bug in it. Assuming there is no UB and optimizing under that assumption gives a speedup. You get the speedup whether that assumption is wrong or not. If that assumption is wrong, your program might also blow up in hilarious ways. But in no way does making the assumption that your code has no undefined behavior rely on your code having undefined behavior.
- mpweiher 5y ago> and reliably corrupt memory Who said memory corruption had to occur "reliably"?? "Permissible undefined behavior ranges from ignoring the situation completely with unpredictable results" > compilers can't allocate local variables in register. Why not? > If it's on stack, out-of-bounds write may reliably corrupt stack, so how can it be on register? "Permissible undefined behavior ranges from ignoring the situation completely with unpredictable results" > And I am yet to see any good answer. What would qualify an answer as "good" IYHO?
- indymike 5y ago>> and reliably corrupt memory > Who said memory corruption had to occur "reliably"?? Usually when I write code that corrupts memory, it reliably corrupts memory an unreliable way.
- jwatte 5y agoThe concept of "friendly C" is not new, and this is one of the straw men that it knocked down, because it is actually possible to define "what I mean." It's totally OK to say that an out-of-bounds write may or may not be detectable by reading any particular other local or global variable or heap value. That doesn't in turn translate to compilers being allowed to turn a bug in a SPECint kernel into an empty infinite loop (a case that happened!)
- sanxiyn 5y agoSo what is the definition? That's the entire issue. If the definition is -fno-strict-overflow -fno-strict-aliasing, we are in violent agreement. We should consider making it standard. If the definition is for C to be free of UB, we are in violent disagreement, that would completely ruin C's performance.
- pjmlp 5y agoNot surprising that is the only way it managed to win the performance game against safer systems programming languages. "Oh, it was quite a while ago. I kind of stopped when C came out. That was a big blow. We were making so much good progress on optimizations and transformations. We were getting rid of just one nice problem after another. When C came out, at one of the SIGPLAN compiler conferences, there was a debate between Steve Johnson from Bell Labs, who was supporting C, and one of our people, Bill Harrison, who was working on a project that I had at that time supporting automatic optimization...The nubbin of the debate was Steve's defense of not having to build optimizers anymore because the programmer would take care of it. That it was really a programmer's issue.... Seibel: Do you think C is a reasonable language if they had restricted its use to operating-system kernels? Allen: Oh, yeah. That would have been fine. And, in fact, you need to have something like that, something where experts can really fine-tune without big bottlenecks because those are key problems to solve. By 1960, we had a long list of amazing languages: Lisp, APL, Fortran, COBOL, Algol 60. These are higher-level than C. We have seriously regressed, since C developed. C has destroyed our ability to advance the state of the art in automatic optimization, automatic parallelization, automatic mapping of a high-level language to the machine. This is one of the reasons compilers are ... basically not taught much anymore in the colleges and universities." -- Fran Allen interview, Excerpted from: Peter Seibel. Coders at Work: Reflections on the Craft of Programming So it was time to embrace UB was means to recover ground, and here we are back at hardware memory tagging and sanitizers as ultimate mitigations for a patient that will never recover from its bad habits.