It seems like a really obvious feature that's easy to support but isn't.
It seems like a really obvious feature that's easy to support but isn't.
# [16] c := a + b;
movq U_$P$TEST_$$_A(%rip),%rdx
movq U_$P$TEST_$$_B(%rip),%rax
addq %rdx,%rax
jno .Lj9
call FPC_OVERFLOW
.Lj9:
movq %rax,U_$P$TEST_$$_C(%rip)
It should be easy to support in C/C++, as FPC uses the same LLVM backend.How can you use memory addressing modes to do math, like 987*765?
Lol. See for yourself if you don't believe me.
https://godbolt.org/z/o14f7cdsc
> Why would a compiler use memory access to do math?
Because there are efficient instructions for it. Have you written an arithmetic code generation pass? It's normal to do it like this.
> Math operations are register based, none of the quantities are addresses.
An address is just a number. Yeah they're in registers or can be immediate. So what?
> How can you use memory addressing modes to do math, like 987*765?
Because AMD64 addressing modes have a base, a scale, and an offset component. That's multiplying and adding.
Let me know how you think you can work out if that lea overflowed!
You can use -ftrapv to check for integer overflows, like this
https://godbolt.org/z/snbdadv8b
>Let me know how you think you can work out if that lea overflowed!
LEA (Load Effective Address) can't overflow... memory is supposed to wrap, the byte after the last byte is byte 0.
> LEA (Load Effective Address) can't overflow...
You're mistaken - it's defined to overflow.
https://reverseengineering.stackexchange.com/questions/11442...
RISC-V architects weighted pros and cons of having a flags register, and pros and cons of having overflow exceptions.
They concluded it is best to not have flags at all (conditional branches do their own testing, and no flag dependencies need to be tracked which simplify superscalar implementations) nor overflow checks (flow breaking and costly; if you need the check, the cost of a software check is minimal, by design).
It also doesn't look like to me the cost of a software check can always be trivial. It can be for a single operation, but an advantage of an overflow register is that it allows to check for a group of operations as a whole (check/branch just once and abort), which is what is probably practical to do algorithmically. In such scenario switching to software checks for each op and/or bound check the inputs sounds by far not minimal.
On i8086 at least a non-taken conditional jump is 4 cycles while a unconditional jump is 15, for a total of 19 cycles, while a taken conditional jump is only 16. So it makes sense to almost always take the conditional jump.
[1]: https://wiki.freepascal.org/Free_Pascal_supported_targets
[2]: https://edge.edx.org/c4x/BITSPilani/EEE231/asset/8086_family... (page 2-45, or 60 in the PDF)
[3]: page 2-58 in[2] or 73 in the PDF
The issue is that unsigned integers must not trap on overflow, and CPUs, if they could be configured to trap on arithmetic overflow, usually do not have separate opcodes for overflowing arithmetic operations and trapping arithmetic operations.
For example with gcc one should always use these compile options, unless there exists an extremely serious reason to do otherwise:
-fsanitize=address,undefined -fsanitize-undefined-trap-on-error
These options provide correct behavior not only on overflows, but also on out-of-bounds accesses and other C undefined operations.
That is what I have meant by "an extremely serious reason to do otherwise".
For many programs the speed is determined by things like the throughput or latency of accessing the main memory, or by I/O operations, or by interaction with an user, in which case the sanitize options have negligible influence on speed.
When necessary, they should be turned off only for specific well tested functions whose performance is limited by CPU computation in registers or in cache memory, and they should remain on for the rest of the program.
I understand the existing undefined behavior, that's not particularly interesting. What would be interesting is well-defined behavior for new types that does not interfere with however defined or undefined behavior exists for the existing types.
> usually do not have separate opcodes for overflowing arithmetic operations and trapping arithmetic operations.
I don't care how good or bad the codegen is. Just emit the code for the right behavior, because I'm using this type that requires this behavior.
You forgot to add 'in C', in Zig they have the same behaviour as signed integers.
What do you see happening in that case?
> However, because compilers love exploiting undefined behavior for speedups
Except if the behavior had been well defined any attempt to catch overflow would violate the standard - see unsigned integers for reference.
> so it's actually fairly slow on modern processors.
Are you sure that having a potential branch every second operation, that depends directly on the result of the preceding operation, has at any point in time not been horribly slow?
Correct.
> Except if the behavior had been well defined any attempt to catch overflow would violate the standard - see unsigned integers for reference.
Then the compiler could emit them only on signed operations, but they don't.
> Are you sure that having a potential branch every second operation, that depends directly on the result of the preceding operation, has at any point in time not been horribly slow?
Yes; it's been shown that `INTO` is slower than `JO raiseOverflowError` because, by the nature of `INTO` being an interrupt, it doesn't get the advantage of branch prediction, but `JO` would.
On an older processor without a pipeline, sure, it'd be just as slow as an equivalent check, but with how massive the pipelines are on today's processors, it's bad.
A CPU could predict INTO — simply predicting it as not taken would be a good guess. This would require designing, implementing, verifying, and maintaining, and it might add some area to important parts of the CPU. The vendors haven’t seen it as a priority.
(AIUI at least older CPUs couldn’t predict branches in microcode, and INTO is presumably fully microcoded, so it doesn’t get predicted.)