llvm / llvm/llvm-project

[Clang 23][Windows] Changed overlapping-load comparison versus Clang 22 (possible source alignment UB)

Open
#221,938 0 comments 0 reactions 0 assignees View on GitHub
clang
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.