> 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.
> We trap on division because it is objectively a programmer error that has no obvious right answer.
Defining (2^31 - 1) + 1 = -2^31 is no more of an "obvious right answer" than defining N/0 = 42 for all N. It's just one that we computer engineers have been trained to accept because it is occasionally useful.
But it's still madness which discourages us from using tools that emit more correct code.
> A consistently not taken branch has very, very close to 0 overhead in a modern processor.
Except the half a dozen or so bytes it consumes in the cache.
Nevertheless, I concede that others have thought about this a lot more than I have and done so with hard data in front of them.
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.