# Compiler generating breakpoint instructions for simple maths

**URL:** <https://forums.riscstar.com/t/compiler-generating-breakpoint-instructions-for-simple-maths/15>\
**Category:** Toolchain\
**Created:** [November 25, 2025, 2:49pm UTC](https://forums.riscstar.com/t/compiler-generating-breakpoint-instructions-for-simple-maths/15 "2025-11-25T14:49:59Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![Daniel](https://forums.riscstar.com/user_avatar/forums.riscstar.com/daniel/32/32_2.png) [@Daniel](https://forums.riscstar.com/u/Daniel)\
**Post date:** [November 25, 2025, 2:49pm UTC](https://forums.riscstar.com/t/compiler-generating-breakpoint-instructions-for-simple-maths/15/1 "2025-11-25T14:49:59Z")

</div>

If you read this expecting a compiler bug then prepare to be disappointed! This is just an _“I didn’t know the compiler did that”_ observation that I stumbled across today whilst handling an oops report. I thought it was interesting.

So…

If we hand the following C to the GNU C compiler (including the RISCstar toolchain) then it will generate breakpoint instructions (or “undefined” instructions on x86-64):

```c
int f(int x, int y) {
    return x / (y ? __builtin_clz(y) : 0);
}

```

The ternary would normally be an inline function but has been fully expanded in the example above. It has been defensively coded to avoid calling `__builtin_clz(0)` (which is undefined). Sadly in our case the output is used as a divisor meaning that, although the ternary has a defined value, there is still a divide-by-zero if y is zero… and that is also undefined.

Compilers can do pretty much anything when there is undefined behaviour (including removing the zero check entirely) but gcc’s behaviour is interesting. It recognises there is no point in generating any real code to implement behaviour that is known to be undefined so it generates a breakpoint instead!

```auto
f:
        beq a1,zero,.L7 # Branch to .L7 if argument 1 is zero
        clzw a1,a1 # Count Leading Zeros in Word 
        divw a0,a0,a1 # Divide Word
        ret
.L7:
        ebreak # Breakpoint

```

Doing a little archaeology led me to discover that gcc has been doing this since gcc 7.1 was released in 2017. However I’ve never seen it before. Perhaps I just don’t write enough undefined behavior bugs!

Anyhow, if you actually do find this is interesting, this is the Compiler Explorer sandpit I used to examine things a little more:

> **[Compiler Explorer](https://godbolt.org/#z:OYLghAFBqd5QCxAYwPYBMCmBRdBLAF1QCcAaPECAMzwBtMA7AQwFtMQByARg9KtQYEAysib0QXACx8BBAKoBnTAAUAHpwAMvAFYTStJg1AB9U8lJL6yAngGVG6AMKpaAVxYMQAJlIOAMngMmABy7gBGmMQgAMykAA6oCoS2DM5uHnoJSTYCAUGhLBFRXBaYVjkMQgRMxARp7p4%2BlpjWKVU1BHkh4ZExFtW19RklCgOdgd2FvVwAlBaorsTI7ByBBADUVBBr66qk6zsAnjPrAKQA7ABCpxoAgusP68SYBIsMu%2BsA9OsQh2fRADF1qYwq46DYGMZkLQAF6/E4gdYaGanaLXO4XAAiHDmtE4AFZeJ4OFpSKhOI51goFktMGcvNEeKQCJocXMANYgSQANgAdABOST4rjc/nnLhec786LnaR4jiSImssmcXgKEAaZmsuZwWBINAsOJ0SLkSgGo30KLEABu3MkAFpkMBkMguPivJqaLQCJF1RAwsqwoEaodOEyg8xiIcAPJhbQtFncXgGtiCaMMWihkm8LCg4COMS0dVJ0hYFiGYDibOlvDPVrWzDF0mYVQtVw%2BsO8NZlZW0PBhYgh5xYZUEYh4Fid0gN4hhRKYTGYctGPtGbV8AzABQANTwmAA7tG4owp/xBCIxOwpDJBIoVOpq7ofAY16Yofp%2B%2BrIHNUHEKsX7WjLx1ntcslgQVFMRtO1nWtYwYTCJh4LCMJeFQGdxywL8IDmOJDBCVgVnWAAlABJIRHHtbcfjte0gwIGY1TKBMUnsBgnBcBoJHxXx2K6AoihAc54kSZIBCGTwSiyMSGH4npilKco2jGCTuMUliBHaWo5KmKJhNGDpVJGMYdME2Z5kWZYJFxAklWrckOBI8jKOoiBaPoxitWzHV9VQQ1jTICgIHNAKUBqFgYLdD0%2BHBX1KADasIxDKckqjWN42sKcU0YAh00zZVc1cfNC2LJkywrKtSXwOsbAbJteBbNsOxLbt5VJPsByHDAVlJMcJynGc5yURdl0rQJQG8jcmC3XcDyPE8SzPYRRHEa8lrvNRlV0WIXxMMwPzCbCfz/FIAKAtCMLwLD4Fw/DgkIkBblucC7XU1o7AgBwjN49BTOmETshSb7pIqP6FOad7KhUzjhjeiotPGfJ5L0AzBhhyT%2Bg6MHrIs2kcflQlSGJUlySel7pETLQfJQPyLRNIKQstFBgCimLvTi/1A2DKMUu5mM4wTLK/NTXKMyzKrMDzAtaCLKdypXHqc1rFi6uVRrkHbFYmVa3t%2B0HKNh0V5lx0nEtBvnEaKvG9cqE3Hc90PY9iSZJaL1W6R1qUTbHz0XaQDfcwOqOskToEM6vAuyJMMbG74juh7HNUAAObl7TtJilI%2Br70b0fwJgE/6QaBnOpNE0H8%2BRkZmMhhHvoh%2BGTIr3SUeh9IMdRxHJjMuZqUsq8bI4QnibQzhdhTtPFS8qmORAfFol5DR8UkJPog0Lwk/OD1uUlfROEn4eVQ4NUNSntld44COieVBzeEps%2BZySOxJCAA%3D%3D)**
>
> Compiler Explorer is an interactive online compiler which shows the assembly output of compiled C++, Rust, Go (and many more) code.

---

<div class="post-metadata">

**Author:** ![Guodong\_Xu](https://forums.riscstar.com/user_avatar/forums.riscstar.com/guodong_xu/32/28_2.png) [@Guodong\_Xu](https://forums.riscstar.com/u/Guodong_Xu)\
**Post date:** [November 28, 2025, 10:21am UTC](https://forums.riscstar.com/t/compiler-generating-breakpoint-instructions-for-simple-maths/15/2 "2025-11-28T10:21:31Z")

</div>

what happens if you throw this code to gcc before 2017? will it generates a div/0 code? 😉

---

<div class="post-metadata">

**Author:** ![Daniel](https://forums.riscstar.com/user_avatar/forums.riscstar.com/daniel/32/32_2.png) [@Daniel](https://forums.riscstar.com/u/Daniel)\
**Post date:** [November 28, 2025, 11:02am UTC](https://forums.riscstar.com/t/compiler-generating-breakpoint-instructions-for-simple-maths/15/3 "2025-11-28T11:02:59Z")

</div>

Pretty much, yes. We actually have to switch to A64 assembler to see what the codegen looks like for older gcc versions since the (upstream) RISC-V support doesn’t go back that far.

So for A64 recent gcc compilers compile that fragment to:

```auto
f:
        cbz w1, .L6
        clz w1, w1
        sdiv w0, w0, w1
        ret
.L6:
        brk #1000

```

Whilst gcc 6 does the divide-by-zero via a conditional select:

```auto
f:
        clz w2, w1
        cmp w1, 0
        csel w1, w2, w1, ne
        sdiv w0, w0, w1
        ret

```

x86-64 is similar to A64 but using a branch rather than a conditional select in gcc 6.

Finally, if you are curious, clang simply assumes it doesn’t need to worry about the undefined case (which it doesn’t, it is undefined) and removes the zero check entirely. That means it ends up dividing by whatever the clz (or equivalent) instruction returns on 0 (which varies depending on instruction set).

---

<div class="post-metadata">

**Author:** ![Guodong\_Xu](https://forums.riscstar.com/user_avatar/forums.riscstar.com/guodong_xu/32/28_2.png) [@Guodong\_Xu](https://forums.riscstar.com/u/Guodong_Xu)\
**Post date:** [November 28, 2025, 11:46pm UTC](https://forums.riscstar.com/t/compiler-generating-breakpoint-instructions-for-simple-maths/15/4 "2025-11-28T23:46:41Z")

</div>

Interesting. I tried to understand the arm assembly code, especially what will happen ‘sdiv’ divide-by-zero. The pseudo code in Arm’s [website](https://developer.arm.com/documentation/ddi0403/d/Application-Level-Architecture/Instruction-Details/Alphabetical-list-of-ARMv7-M-Thumb-instructions/SDIV#:~:text=Signed%20Divide%20divides%20a%2032,code%20flags%20are%20not%20affected.) actually saying the behavior of ‘sdiv’ devide-by-zero is undefined (aka. implementation dependent). GCC’s implementation, insertion of ‘ebreak’, unified the behavior, which is pretty good.

```auto
if ConditionPassed() then
    EncodingSpecificOperations();
    if SInt(R[m]) == 0 then
        if IntegerZeroDivideTrappingEnabled() then
            GenerateIntegerZeroDivide();
        else
            result = 0;
    else
        result = RoundTowardsZero(SInt(R[n]) / SInt(R[m]));
    R[d] = result<31:0>;

```
