On an embedded project, I recently had to debug a crash where the root cause was a division by zero. The offending code in question reduced to something like function foo()
presented here.
To validate that it actually was a division by zero problem, I added the if block that printed "GOTCHA". After this change, I saw that the processor status register (CFSR) had the UNDEFINST
bit set instead of the exepected DIVBYZERO
bit. What?
Using godbolt we can see the compiler emitting an instruction 0xdeff
in main... with no other code (suggesting that the compiler realized that the code will not work and simply stopped -- without warning, I might add).
So if we uncomment the if block, we can see that the compiler generates complete code for main()
. Instead, it generates the illegal 0xdeff
instruction on the printf side of the if block in foo()
(having optimized the printf of a constant string to a puts).
And this illegal instruction explains why I was seeing the UNDEFINST
bit in the CFSR.
Maybe not very much. Or maybe a bunch. Which is kind of the problem. Other ARM core/compiler combinations (clang, for example) quietly return zero on division by zero (the mechanics may still involve processor traps but the application survives). In my particular application, it caused a crash in device firmware, in code that runs many times per second and where an incorrect calculation wouldn't affect anything. This crash would have been particularly mistifying if we had configured the core to ignore division by zero (which is an option).
This is fun on Apple Silicon just to show the different handling between arm64 and x86_64, compiling with that block un-commented: