[Clang 23][Windows] Changed overlapping-load comparison versus Clang 22 (possible source alignment UB)
- Dominant language
- LLVM
- Stars
- 40.5k
- Forks
- 18.7k
- PR merge metrics
- PR metrics pending
Description
# Clang 23 changes overlapping-load comparison on Windows (possible source alignment UB)
## Summary
On Windows x64, the standalone program below prints `PASS` with Clang 22.1.8, but throws `type hash collision` with Clang 23.1.0 at `-O2` and `-O3`. It was reduced from a short-string equality check used during application startup. No project or third-party headers are needed.
**Important qualification:** this is a suspected regression / request for clarification, not a claim that the source is free of undefined behavior. The original load idiom places `aligned(1)` on a pointer variable, not on the pointed-to type, and dereferences a union pointer into character storage. There may be alignment, aliasing, or lifetime UB. Replacing the load with `memcpy`, or using an `aligned(1), may_alias` integer typedef, makes Clang 23 pass. If this is expected optimization of invalid source, please close this as such; confirmation would help distinguish a library defect from a compiler defect.
## Reproducer
```cpp
#include
#include
#include
#include
union Word { unsigned long long value; char bytes[8]; };
inline unsigned long long load(const char* pointer)
{
__attribute__((aligned(1))) const Word* word = reinterpret_cast(pointer);
return word->value;
}
struct View {
const char* data;
std::size_t size;
};
inline bool equal(View a, View b)
{
return a.size == b.size && load(a.data) == load(b.data)
&& load(a.data + a.size - 8) == load(b.data + b.size - 8);
}
template View name()
{
static constexpr char text[] = "ModuleMapView";
return { text, 13 };
}
struct Slot { View type; void* storage = nullptr; };
template void validate(Slot& slot)
{
if (!equal(slot.type, name()))
throw std::logic_error("type hash collision");
}
template __declspec(noinline) void construct(Slot& slot)
{
if (!slot.storage)
slot.storage = ::operator new(sizeof(T));
validate(slot);
new (slot.storage) T { 42 };
}
int main()
{
Slot slot { name() };
try {
construct(slot);
std::puts("PASS");
} catch (const std::exception& error) {
std::puts(error.what());
::operator delete(slot.storage);
return 1;
}
::operator delete(slot.storage);
}
```
Build and run in a Windows environment with the MSVC standard library/toolchain available:
```powershell
clang++ --target=x86_64-pc-windows-msvc -std=c++23 -O3 repro.cpp -o repro.exe
.\repro.exe
```
The intended result is `PASS` and exit code 0. Clang 23.1.0 instead prints `type hash collision` and exits with code 1.
## Compiler Builds
Both compilers identify their source repository as `https://github.com/llvm/llvm-project`:
- Clang 22.1.8: `ca7933e47d3a3451d81e72ac174dcb5aa28b59d1`
- Clang 23.1.0: `ea7d852a70e8bdfaf601d6626a760f9771b2c4b4`
- Host and target: Windows x64, `x86_64-pc-windows-msvc`
## Verified Results
| Load implementation / optimization | Clang 22.1.8 | Clang 23.1.0 |
| --- | --- | --- |
| Original, `-O0` | PASS | PASS |
| Original, `-O1` | PASS | PASS |
| Original, `-O2` | PASS | FAIL |
| Original, `-O3` | PASS | FAIL |
| `memcpy`, `-O3` | PASS | PASS |
| `aligned(1), may_alias` typedef, `-O3` | PASS | PASS |
The `memcpy` alternative copies `sizeof(unsigned long long)` bytes into a local integer and returns that integer. The typedef alternative is:
```cpp
typedef unsigned long long UnalignedWord __attribute__((aligned(1), may_alias));
inline unsigned long long load(const char* pointer)
{
return *reinterpret_cast(pointer);
}
```
The larger application-side reproducer also fails with Clang 23 using `-O3 -fno-strict-aliasing`, while Clang 22 passes. In the actual startup function's assembly, Clang 22 uses distinct expected 64-bit constants for the string's windows at offsets 0 and 5 (`0x614D656C75646F4D` and `0x7765695670614D65`). Clang 23 reuses the first constant for both comparisons. I have not established the first affected LLVM commit or tested the reduced case on Linux.
Contributor guide
Research direction
Start by building the standalone reproducer with the supplied Windows x64 clang++ command and compare Clang 22.1.8 with Clang 23.1.0 at -O2 and -O3. Review the original load implementation alongside the memcpy and aligned(1), may_alias alternatives. Done means determining whether the behavior is a valid-source compiler regression or undefined behavior, and, if it is a regression, identifying the first affected LLVM commit.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp
- Domain
- compilers, operating-systems
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100