7 ms·
It really seems like the compiler writers have gone insane in the last decade or so. Any reasonable person would look at the standard and conclude the intent i
by xscott 4y ago
It really seems like the compiler writers have gone insane in the last decade or so. Any reasonable person would look at the standard and conclude the intent is to describe what is required to write super-portable programs. Yes, you could have 1s-complement or sign-magnitude integers, so you can't rely on signed overflow for portable code. Yes, you could have separate address spaces for floating point and integers, so you can't rely on cross-type pointer casts or address arithmetic for portable code.
But the compiler writers are using "undefined behavior" as justification to break code on machines that don't have weird address spaces and don't use 1-s complement integers.
So the result... anyone who wants C/C++ to just be a high level assembler is going to need to do all their arithmetic with unsigned and all their pointers as char. Say goodbye to readability or type safety.
- bonzini 4y agoThere are lots of real-world cases where type-based alias analysis produces not just better, but even just "decent" code. Example: void add_all(float **x, float **y, int m, int n) { for (int i = 0; i < m; i++) for (int j = 0; i < n; i++) x[i][j] += y[i][j]; } without TBAA cannot be optimized to void add_all(float **x, float **y, int m, int n) { for (int i = 0; i < m; i++) { float *xi = x[i], *yi = y[i]; for (int j = 0; i < n; i++) xi[j] += yi[j]; } Or for overflow, "x[i] = 0;" cannot be compiled to "movl [x+RAX*4], 0" if i is an "unsigned int".
- xscott 4y ago> Or for overflow, "x[i] = 0;" cannot be compiled to "movl [x+RAX*4], 0" if i is an "unsigned int". This is another pet peeve of mine. Why does almost every justification about UB and signed overflow involve using a 32 bit integer on a 64 bit platform? Change those int variables to ssize_t (or size_t if you must), and it's good. Instead of creating and jumping through insane hoops to improve bad code, they should warn you that there are performance costs for using the wrong sized integer as an array subscript. Edit: Oops, I missed the point on the first one. So I removed a bad example.
- danuker 4y ago> using a 32 bit integer on a 64 bit platform? Not about integers, but in general: maybe 64-bit is overkill. The trend in ML has been to reduce size: "We have demonstrated DNN training [using nVidia GPUs] with 8-bit floating point numbers (FP8) that achieves 2−4× speedup [compared to FP32 computation] without compromise in accuracy [on most CNN/DNN test accuracy degradation is ~0% stochastically, but max 2% on specific ImageNet runs]." https://arxiv.org/abs/1812.08011 https://arxiv.org/abs/1812.08011
- xscott 4y agoIt's not obvious unless you read and think about the assembly, but bonzini's point was that using a 32 bit signed int (which is not allowed to overflow) means the compiler can cleverly promote it to use register RAX as an index without any other shenanigans to deal with 32 bit overflow. RAX is a 64 bit register for integers or pointers. There are reasons why this might be advantageous over using EAX, which is the name for the low 32 bits of RAX. It's very possible that bonzini is referring to the example in the following link: https://www.youtube.com/watch?v=yG1OZ69H_-o&t=2357s https://www.youtube.com/watch?v=yG1OZ69H_-o&t=2357s Back to your overall point though, you're right that there's frequently an advantage to having your bulk data use smaller data types. It's better for caching and memory bandwidth, and it opens up more possibilities for parallelization with SIMD or GPU magic. However, it's really a false economy for things like loop indices. It doesn't matter if you declare your variable to be int8_t using just 1 byte, because it's probably going to tie up an entire 64 bit register anyways. There are ways to use only parts of registers, so you can pretend you have more registers, but these cause dependency stalls and are generally not good for modern processors, or at least not a win for speed. To a lesser extent, it's a false economy with floating point on the CPU too. Your 32 bit float is likely taking up an entire 128 bit (or larger) SIMD register, and the operations generally have the same latency and throughput as their 64 bit (double) equivalent. The rules can be very different on a GPU of course.
- bonzini 4y ago> There are reasons why this might be advantageous over using EAX, which is the name for the low 32 bits of RAX. EAX is not available in addresses. > It's very possible that bonzini is referring to the example in the following link Not intentionally but yes.
- deleted 4y ago[deleted]
- renox 4y agoBah, for the best optimisation, you need restrict (https://en.wikipedia.org/wiki/Restrict https://en.wikipedia.org/wiki/Restrict) anyway..
- pif 4y ago> Any reasonable person would look at the standard and conclude the intent is ... Any reasonable person would look at the standard and conclude there is no other implicit intent than to describe a standard. > anyone who wants C/C++ to just be a high level assembler They never got the right to pretend it, as C/C++ was never meant to be that. Sure, the first (several) versions of the compiler were not as smart as modern ones, but any reasonable person who would have looked at the standard should have understood since day 1 what all this was about. > the compiler writers are using "undefined behavior" as justification to break code on machines that ... If you are reasoning on any other machine that the C virtual machine, _you_ are wrong. If you want to reason about X86, use assembly. Higher level languages are not coupled to any particular hardware.
- xscott 4y ago> Any reasonable person would look at the standard and conclude there is no other implicit intent than to describe a standard. That's complete nonsense. If there wasn't intent, it would just be random words making random rules. There was a reason and motivation behind the creation of those rules, and reasonable people can at least speculate about the motivation. > If you are reasoning on any other machine that the C virtual machine, _you_ are wrong. The underscore is very dramatic. :-) > If you want to reason about X86, use assembly. Higher level languages are not coupled to any particular hardware. I never said anything about X86. I think maybe you're reliving an argument with someone else. However, all of i686, AMD64, ARM, AArch64, PowerPC, RISC-V, Alpha, PA-RISC, or any mass market server, desktop, laptop, or smartphone in the last 30 years have one address space and use 2's complement for signed integers. It's a choice for a high level language to pretend that's not true.
- jcranmer 4y agoThe semantics of "restrict", which made it into C99, inherently require the anything-goes interpretation of UB to have the keyword make any sense. These means your "faulty" definition of undefined behavior is at least 23 years old. If you dig a little deeper, it has to be even older. The semantics of unions were changed in C99 so as to make type punning via unions no longer UB--which at least implies that people were thinking about the optimization effects of UB even earlier. The other thing to note was that if UB were "just" about portability to "weird" machines... why did the C committee choose to make it undefined instead of implementation-defined? The specification already has a tool that gives compilers flexibility for weird architectures; not using that tool suggests that the committee didn't want to require the compiler to do something "reasonable."
- electroly 4y agoYour question seems to be rhetorical and assumes that I know the answer, but I don't. Why did they choose to make all this stuff UB instead of implementation-defined? I don't really understand why we have UB at all instead of all of that stuff being implementation-defined. Other languages have far fewer things that they consider UB, and that seems better to me. Why was having a large swath of UB considered better?
- andi999 4y agoI think historically the idea behind UB was to allow to write a compliant compiler (for different platforms) as easily as possible. (so to decrease the burden on the compiler writer). This has worked. Why they kept this approach? I dont know.
- jcranmer 4y agoThere are a few reasons to make things UB. One of the big reasons is--drumroll--to enable compiler optimizations. The other big reason is some variation on the problem that actually coming up with reasonable semantics is difficult to impossible, and often one of the factors that makes such semantics difficult is again because of compiler optimizations. I think there's a big misconception here that the use of UB to drive optimizations is somehow a perversion of the original design for C, but the evidence strongly points away from that. In particular, strict aliasing rules don't really make sense unless you're going to use them to afford optimization opportunities, and those were present as far back as C89. I will additionally point out that any C-like language is going to have a large kernel of necessarily UB to permit any optimization whatsoever (namely, UB in relation to memory unsafety) [1]. The things in C where people most complain about UB--namely signed integer overflow and strict aliasing--also happen to be the ones where there's no good reason to interpret C89's decision to make them UB instead of something lesser as anything other than letting compilers optimize them because they're UB. Even if it seems to be gratuitous (and I would personally argue that it is), it is very much intentional. [1] Yes, even Rust makes this UB.
- manwe150 4y agoYou cannot anymore. "There is One True Representation for signed integers, and that representation is two’s complement" (P0907R4)
- xscott 4y agoThis part is telling: > If a signed operation would naturally produce a value that is not within the range of the result type, the behavior is undefined. The author had hoped to make this well-defined as wrapping (the operations produce the same value bits as for the corresponding unsigned type), but WG21 had strong resistance against this. So, even within the standards group there are disagreements on the topic.