Trapping integer division by zero

534 views
Skip to first unread message

Nicolas Boichat

unread,
Dec 30, 2020, 10:11:59 PM12/30/20
to sw-...@groups.riscv.org, Aseda Aboagye, Tzung-Bi Shih, Yilun Lin
Hi RISC-V folks,

We are using a RISC-V-based microcontroller as an embedded controller,
and found out that RISC-V behaves differently from most of our other
architectures when performing an integer division: the core just
returns 0xffffffff, and does not raise any kind of exception.

The ISA manual [1] clearly states that's expected:
"""

We considered raising exceptions on integer divide by zero, with these
exceptions causing a trap in most execution environments. However,
this would be the only arithmetic trap in the standard ISA
(floating-point exceptions set flags and write default values, but do
not cause traps) and would require language implementors to interact
with the execution environment’s trap handlers for this case. Further,
where language standards mandate that a divide-by-zero exception must
cause an immediate control flow change, only a single branch
instruction needs to be added to each divide operation, and this
branch instruction can be inserted after the divide and should
normally be very predictably not taken, adding little runtime
overhead.
"""

For safety, we'd actually like to catch those divisions by zero in our
codebase, so we'd like the compiler to emit those check instructions.

GCC does have a `-mcheck-zero-division` option, but that's only
available for MIPS. We ended up using UBSAN's
`-fsanitize=integer-divide-by-zero` to instrument these divisions [3].
Combined with LTO, the additional code is actually quite reasonable,
and performance impact is negligible, but this still feels like a
hack...

Are we the first ones to hit this issue? Has somebody already started
working on adding compiler (GCC or LLVM) support for these checks? I
feel that Linux userspace applications would expect SIGFPE to be
emitted in those cases... Or is the common thinking that it's ok to
ignore those divisions by zero? (and that the code itself should
perform the checks where required)

Thanks!

Best,

Nicolas

[1] http://www.five-embeddev.com/riscv-isa-manual/latest/m.html
[2] https://gcc.gnu.org/onlinedocs/gcc-10.2.0/gcc/MIPS-Options.html#MIPS-Options
[3] https://crrev.com/c/2605039

Andrew Waterman

unread,
Dec 30, 2020, 10:27:21 PM12/30/20
to Nicolas Boichat, RISC-V SW Dev, Aseda Aboagye, Tzung-Bi Shih, Yilun Lin
Speaking only to the Linux side of things, SIGFPE on integer division by 0 is an x86-ism--or, at least, that's where it's most prevalent.  RISC-V definitely isn't the only ISA where Linux application code doesn't raise SIGFPE.  Of course, since this is undefined behavior in C, both outcomes are considered valid.

Although I think many people would prefer the default to remain as-is for code-size and perf reasons, I can certainly understand the desire for a -mcheck-zero-division option.  It  could be implemented in a fashion similar to MIPS, but with a branch over a trapping instruction instead of with a conditional-trap instruction, since we don't have those.  This isn't already in the works, AFAIK, so you might have to be the ones to contribute it.

In the meantime, using UBSAN seems pretty pragmatic!

--
You received this message because you are subscribed to the Google Groups "RISC-V SW Dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to sw-dev+un...@groups.riscv.org.
To view this discussion on the web visit https://groups.google.com/a/groups.riscv.org/d/msgid/sw-dev/CANMq1KC1M%2BH_gs0f4x_MEnqKuJ_3OC_AusfZ%3Dj1x94_%3D5LmrZA%40mail.gmail.com.

Nicolas Boichat

unread,
Jan 4, 2021, 7:17:34 PM1/4/21
to Andrew Waterman, RISC-V SW Dev, Aseda Aboagye, Tzung-Bi Shih, Yilun Lin
Thanks for the quick reply Andrew, we'll try to see if we can spend
time on this in the coming months.

Best,

Nicolas Boichat

unread,
Jan 8, 2021, 3:16:12 AM1/8/21
to Andrew Waterman, RISC-V SW Dev, Aseda Aboagye, Tzung-Bi Shih, Yilun Lin, George Burgess
+George Burgess
George (cc-ed) suggested to add -fsanitize-undefined-trap-on-error,
and the generated code is really small (no LTO required):
80025aec: 0716 slli a4,a4,0x5
80025aee: e291 bnez a3,80025af2 <st_normalize+0x66>
80025af0: 9002 ebreak
80025af2: 02d74733 div a4,a4,a3
80025af6: 00092683 lw a3,0(s2)

We lose specificity in the error reporting though (the crash appears
to be a breakpoint), but that's ok, as the exception contains the PC,
so we can figure out what actually went wrong.

So, probably not going to do much more work on this, as this fits our
embedded use case as optimally as it gets.

Thanks!

Andrew Waterman

unread,
Jan 8, 2021, 6:02:21 AM1/8/21
to Nicolas Boichat, RISC-V SW Dev, Aseda Aboagye, Tzung-Bi Shih, Yilun Lin, George Burgess
Cool.

An obvious optimization is to initiate the division before testing for zero, which on many processors will hide the cost of the zero test.  But for a zero-effort option, I'd say this is a pretty good result :-)
Reply all
Reply to author
Forward
0 new messages