Date: Wed, 30 Apr 2025 13:00:32 -0700
On Wednesday, 30 April 2025 10:23:02 Pacific Daylight Time None via Std-
Proposals wrote:
> 1. To avoid UB with zero divisors, programmers must explicitly check if the
> divisor is zero, introducing conditional branches. In scenarios where the
> divisor is almost always non-zero, this leads to branch prediction
> failures, which may disrupt the pipeline in CPU and degrade the performance
> significantly. (Though "an exception will cost more", but in most case, the
> divisor is not 0, and it will not use any JMP instruction, so it will
> faster. Using exception is a very common way to avoid branch and optimize.)
> 2. Languages like Java and Python throw exceptions when dividing by zero,
> allowing developers to handle such cases elegantly through exception
> handling mechanisms rather than relying on UB.
> These are significantly advantages, aren't they?
No.
Starting with the second point first, those other languages don't count because
they don't compile to machine code (usually). The Python VM and the JVM run
around all of that and can control the behaviour of what happens. Plus, they
don't have other reporting mechanism but exceptions, and they don't have UB in
the first place. And I'm 99.9% sure that they implement this via a check-for-
zero ahead of time.
On the first, catching an issued integer division by zero is extremely hard to
do in modern operating systems. I could do this easily when running on 16-bit
DOS 30 years ago, but today, this will cause a SIGFPE to be sent to the
process. Intercepting such a signal and throwing an exception is technically
possible, but unfeasible: SIGFPE like the other crashing signals belongs to
the application ("the owner of main()"), not the runtime. Installing signal
handlers cannot be done thread-safely and uninstalling signal handlers is
nearly impossible.
To implement this, we'd need the C library's signal() & sigaction() functions
be modified to hide a user-installed SIGFPE handler and bypass it if the
runtime's division-by-zero handling was required. Which in turn means that
your proposed function needs to set a global thread-local variable so the
shadow SIGFPE handler knows when to throw or when to call the user's installed
handler (or crash). This means we're trading off a minor BPU hit caused by a
cheap compare-and-branch for a potentially major TLS block access, with a
minimum of 2 extra instructions.
This is very different from overflow checking for multiplications, additions and
subtractions, because the CPUs set flag bits for those, instead of generating
CPU exceptions.
Proposals wrote:
> 1. To avoid UB with zero divisors, programmers must explicitly check if the
> divisor is zero, introducing conditional branches. In scenarios where the
> divisor is almost always non-zero, this leads to branch prediction
> failures, which may disrupt the pipeline in CPU and degrade the performance
> significantly. (Though "an exception will cost more", but in most case, the
> divisor is not 0, and it will not use any JMP instruction, so it will
> faster. Using exception is a very common way to avoid branch and optimize.)
> 2. Languages like Java and Python throw exceptions when dividing by zero,
> allowing developers to handle such cases elegantly through exception
> handling mechanisms rather than relying on UB.
> These are significantly advantages, aren't they?
No.
Starting with the second point first, those other languages don't count because
they don't compile to machine code (usually). The Python VM and the JVM run
around all of that and can control the behaviour of what happens. Plus, they
don't have other reporting mechanism but exceptions, and they don't have UB in
the first place. And I'm 99.9% sure that they implement this via a check-for-
zero ahead of time.
On the first, catching an issued integer division by zero is extremely hard to
do in modern operating systems. I could do this easily when running on 16-bit
DOS 30 years ago, but today, this will cause a SIGFPE to be sent to the
process. Intercepting such a signal and throwing an exception is technically
possible, but unfeasible: SIGFPE like the other crashing signals belongs to
the application ("the owner of main()"), not the runtime. Installing signal
handlers cannot be done thread-safely and uninstalling signal handlers is
nearly impossible.
To implement this, we'd need the C library's signal() & sigaction() functions
be modified to hide a user-installed SIGFPE handler and bypass it if the
runtime's division-by-zero handling was required. Which in turn means that
your proposed function needs to set a global thread-local variable so the
shadow SIGFPE handler knows when to throw or when to call the user's installed
handler (or crash). This means we're trading off a minor BPU hit caused by a
cheap compare-and-branch for a potentially major TLS block access, with a
minimum of 2 extra instructions.
This is very different from overflow checking for multiplications, additions and
subtractions, because the CPUs set flag bits for those, instead of generating
CPU exceptions.
-- Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org Principal Engineer - Intel DCAI Platform & System Engineering
Received on 2025-04-30 20:00:35