llvm / llvm/llvm-project

Potential undefined behavior in `src_rep_t_clz_impl()` due to missing zero check (`__builtin_clz(0)`)

Open
#167,620 1 comment 0 reactions 0 assignees View on GitHub
compiler-rt:builtins
Dominant language
LLVM
Stars
40.5k
Forks
18.7k
PR merge metrics
PR metrics pending

Description

## **Affected file**
`llvm-project/compiler-rt/lib/builtins/fp_extend.h` (line 69 - 71)

### Code:
```c
static inline int src_rep_t_clz_impl(src_rep_t a) {
return __builtin_clz(a) - 16;
}
```

---

## **Issue description**

This implementation may invoke **undefined behavior (UB)** when `a == 0`.

According to GCC documentation:
> “If x is 0, the result of `__builtin_clz(x)` is undefined.”

Therefore, if `src_rep_t_clz_impl()` is ever called with `a == 0`, the expression `__builtin_clz(a)` would produce undefined results.
Even if current call paths avoid zero inputs, it is safer to protect against this case explicitly, since such UB has led to subtle compiler misoptimizations in the past.

A related example is **[GCC Bug 101175 – "builtin_clz generates wrong bsr instruction"](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=101175)**, where the undefined behavior of `__builtin_clz(0)` caused incorrect code generation on x86 due to instruction combining (BSR/LZCNT).
Add a simple zero guard to prevent undefined behavior:

```c
static inline int src_rep_t_clz_impl(src_rep_t a) {
if (a == 0)
return (int)(sizeof(src_rep_t) * 8); // Return full bit-width for zero
return __builtin_clz((unsigned)a) - 16;
}
```

This ensures:
- No UB when `a == 0`
- Defined, consistent behavior across all platforms
- Maintains existing performance and semantics for valid inputs

---

## **Additional notes**

- The literal subtraction `-16` assumes that `src_rep_t` is 16 bits and that `unsigned int` is 32 bits.
If these assumptions ever change, this hardcoded constant could become incorrect.
Consider deriving it from `sizeof(unsigned)` and `sizeof(src_rep_t)` for portability:
```c
return __builtin_clz((unsigned)a)
- ((sizeof(unsigned) * 8) - (sizeof(src_rep_t) * 8));
```

---

Contributor guide

Open the contributing guide

Research direction

Start in llvm-project/compiler-rt/lib/builtins/fp_extend.h at src_rep_t_clz_impl() and inspect its callers to confirm whether zero can reach the function. Run the relevant compiler-rt tests, then add coverage for zero and verify that the result remains unchanged for nonzero inputs while avoiding undefined behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
c
Domain
compilers
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.