| 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