Issue 203703
Summary [clang] 21.0.0 and 21.1.8 miscompilation of a loop in a C function
Labels clang
Assignees
Reporter Robert-van-Engelen
    Report for clang 21.0.0 and 21.1.8 (tested), suspecting that greater versions such as v22 also have the same issue, judging from Compiler Explorer assembly comparisons. I hope (or hoped) that I did something wrong, but looked at this with others. We strongly suspect that the clang C compiler is miscompiling the simple `list()` function in the attached program that is a greatly simplied version of a Lisp interpeter written in C.

- Observed with clang 21.0.0 on MacOS Apple M1 arm64
- Observed with clang 21.1.8 on Ubuntu in a Docker container on Apple M1 arm64
- There doesn't appear to be undefined behavior in the code.
- The code does not appear to violate the strict aliasing rule.
- When compiled with optimizations (-O1,-O2,-O3) the behavior of the `list()` function is incorrect, see the `list()` function that should return the list `(1 2 3)` but incorrectly returns `()` (nil).

The `list()` function constructs a list `t` using a pointer `p`, initially `t = nil` and `p = &t`, then we repeat 3 loop iterations to add cons pairs to the end of list `t` using pointer `p` at the tail end. This is a common method to extend a list at its tail, e.g. when parsing a list from input, which is `(1 2 3)` in this example:

| step | assign | update `p` | result 
| ---: | --- | --- | ---
| init |   `t = nil`                     | `p = &t` | `t = nil`
| 1   |   `*p = cons(parse(),nil)`      | `p = &CDR(*p) = &cell[ord(*p)]`  | `t = cons(1,nil)`
| 2   |   `*p = cons(parse(),nil)` | `p = &CDR(*p) = &cell[ord(*p)]`  | `t = cons(1,cons(2,nil))`
| 3   | `*p = cons(parse(),nil)`      | `p = &CDR(*p) = &cell[ord(*p)]`  | `t = cons(1,cons(2,cons(3,nil)))`

Then the condition `T(t) == CONS` fails, when `t` should obviously be a cons list.  The compiled code incorrectly gives `t = nil`. Other compilers work fine. Works with `--fno-strict-aliasing` even though there are no pointer accesses to the same memory location through pointers of two different types. There is only one pointer type `L*` which is `double*`. Works when `t` and `*p` are `volatile`, which shouldn't be necessary.

[clang-bug.c](https://github.com/user-attachments/files/28913654/clang-bug.c)
_______________________________________________
llvm-bugs mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-bugs

Reply via email to