4 ms·
Because in the vast majority of cases you don't need to and it would unnecessarily slow down a lot of legitimate computation? You can already detect integer o
by kevinnk 13y ago
Because in the vast majority of cases you don't need to and it would unnecessarily slow down a lot of legitimate computation? You can already detect integer overflow extremely easily, why would the cpu need to trap?
- marshray 13y agoBecause if 0x7FFFFFFF = 2^31 - 1 0x7FFFFFFF + 1 = 0x80000000 = -2^31 then 2^31 - 1 + 1 = -2^31 2^31 + 1 = -2^31 + 1 (adding 1) 2^31 = -2^31 (subtracting 1) 1 = -1 (dividing by 2^31)
- kevinnk 13y agoI think you may not have understood my question. It is _trivial_ to detect integer overflow in virtually every major cpu I've ever seen. x86, ARM, PPC, MIPS, and many, many more all support this in a single instruction. Trapping, on the other hand, is extreamly expensive in modern processors, on the order of tens of thousands of cycles wasted. Furthermore, there are many cases where integer overflow is actually desired. My question is would be why would a cpu trap be a better solution than what we already have?
- marshray 13y agoFor the same reason we trap on integer division by zero. > It is _trivial_ to detect integer overflow I think if we counted the number of + operations in a typical C program and multiplied that by the instruction(s) needed to implement a conditional branch path (often never taken dead code), it would be nontrivial. This overhead should be easily measurable on benchmarks. > Furthermore, there are many cases where integer overflow is actually desired. I'm not arguing against the ability to do that, but I think those cases are far less common. > My question is would be why would a cpu trap be a better solution than what we already have? To allow compilers (not just C) to emit safer code by default by removing the small but measurable instruction penalty.
- deleted 13y ago[deleted]
- kevinnk 13y ago> For the same reason we trap on integer division by zero. We trap on division because it is objectively a programmer error that has no obvious right answer. Division is also a relatively expensive and much less common operation for the cpu. The overhead of adding trapping is much smaller compared to addition. Furthermore, integer overflow has an easy solution: widen the computation. Divide by 0 has no quick answer. > I think if we counted the number of + operations in a typical C program and multiplied that by the instruction(s) needed to implement a conditional branch path (often never taken dead code), it would be nontrivial. This overhead should be easily measurable on benchmarks. A consistently not taken branch has very, very close to 0 overhead in a modern processor. Not to mention that adding trapping to addition would hinder things like instruction reordering and speculative execution, which adds an overhead that can't be disabled when overflow can provably not occur or not matter. The cpu would also have to treat unsigned and signed addition differently, which means an additional opcode, which adds complexity to the control logic and decreases code density. Finally, there are plenty of languages and libraries which do almost exactly what you propose and automatically widen variables when they overflow and do not experience significant slowdown. >I'm not arguing against the ability to do that, but I think those cases are far less common. You are wrong. Consider the pointer arithmetic expression "ptr += x;" on a cpu that has two trapping add instructions (add unsigned and add signed) and ptr is a pointer and x is a signed value. Note that this is extreamly common for things like array access. Suppose the compiler generated an unsigned add. If the ptr value was 0x1 and x was -1 that would generate an overflow because 0x00000001 + 0xFFFFFFFF results in an unsigned overflow. Suppose it instead generated a signed add. If the ptr value was 0x7FFFFFFF and x was 1 this would also generate an overflow because 0x00000001 + 0x7FFFFFFF results in signed overflow. To resolve this you would need 4 different instructions, which would only get worse as you add various operand lengths to the mix. Now I wouldn't say this is a problem for a majority of add instructions, but adding a signed and unsigned number is a very common operation. In addition, if you actually look at generated assembly code, many of the add instructions do not correspond to a '+' but instead do things like stack management or pointer offset adjustment which do not care about overflow because they can be proved to not occur. > To allow compilers (not just C) to emit safer code by default by removing the small but measurable instruction penalty. Again, there is virtually no overhead on a modern out of order, branch predicting processor. And adding trapping does have an overhead, a significant one, that cannot be removed when overflow can be proved to not occur or isn't important. As a final argument, consider this: of all the major cpu architectures I've studied (MIPS, x86, ARM) not a single one has a trapping add instruction. Very smart people have also considered this problem and have come to the same conclusion: trapping is bad and should only be used in truly exceptional circumstances where no recovery is obvious. I agree with you that integer overflow is a big problem in languages like C that do not have a good way to detect it. However, this is truly a language only problem NOT a cpu one.
- azernik 13y ago(Assuming you meant 2^31) The first equation in that second block (and so the rest of the block) doesn't really make sense in 32 bit two's complement arithmetic - there is no 2^31. The real weirdness is that -(-2^31) = -2^31. The fundamental issue here is that computer representations of numbers aren't accurate and don't follow the usual rules, in more ways than just overflow.
- marshray 13y ago> Assuming you meant 2^31 Where? > The first equation in that second block (and so the rest of the block) doesn't really make sense in 32 bit two's complement arithmetic My point exactly. > computer representations of numbers aren't accurate The numbers which can be represented in a computer are perfectly accurate. > and don't follow the usual rules, in more ways than just overflow. At a low level, we program computers to perfom bit operations on registers. Some operations are more straightforward and efficient than others, but ultimately it's how we interpret the bit patterns that makes them "numbers". So if they "don't follow the usual rules", it's our own fault. Security of low-level code has been enough a royal fuckup for long enough now that perhaps we should start to reevaluate some of our traditional assumptions about how CPUs should behave.
- azernik 13y ago>> The first equation in that second block (and so the rest of the block) doesn't really make sense in 32 bit two's complement arithmetic > My point exactly. It doesn't make sense because there's no such number as 2^31 in 32-bit two's complement arithmetic, and I don't think the finite range of finite-length number representations is really a solvable problem. This inevitably leads to a lot of mathematical weirdness, because our math is built on the set of integers being infinite (which is the only kind of set of integers in which closure on addition makes any sense at all). >> and don't follow the usual rules, in more ways than just overflow. > At a low level, we program computers to perfom bit operations on registers. Some operations are more straightforward and efficient than others, but ultimately it's how we interpret the bit patterns that makes them "numbers". Any finite representation of numbers is going to have to break the familiar mathematical rules somehow; I'm pretty satisfied with the tradeoffs made in our current integer representations (unsigned more than signed, but that's in some ways an easier problem).
- deleted 13y ago[deleted]