llvm / llvm/llvm-project

unnamed_addr merges constants with different contents: initialized bytes with uninit bytes

Open
#220,091 2 comments 2 reactions 0 assignees View on GitHub
miscompilation
Dominant language
LLVM
Stars
40.5k
Forks
18.7k
PR merge metrics
PR metrics pending

Description

According to the reference, I would not expect `unnamed_addr` to merge constants with different content (initialized bytes vs uninit bytes). Nevertheless LLVM generates code that allows linker to merge them. For example:

```llvm
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"

@a = private unnamed_addr constant <{ [4 x i8], [4 x i8] }> <{ [4 x i8] undef, [4 x i8] zeroinitializer }>, align 4
@b = private unnamed_addr constant <{ [4 x i8], [4 x i8] }> zeroinitializer, align 4

define i32 @main() {
%1 = icmp ne ptr @a, @b
%2 = zext i1 %1 to i32
ret i32 %2
}
```

```console
$ clang-24 a.ll
$ ./a.out
$ echo "icmp ne ptr @a, @b evaluated to $?"
icmp ne ptr @a, @b evaluated to 0
```

What is perhaps more important, both GVN and SCCP assume that such merging is prohibited:

```llvm
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"

@a = private unnamed_addr constant <{ [4 x i8], [4 x i8] }> <{ [4 x i8] undef, [4 x i8] zeroinitializer }>, align 4
@b = private unnamed_addr constant <{ [4 x i8], [4 x i8] }> zeroinitializer, align 4

define noundef i32 @f(ptr align 4 %p) {
%1 = icmp eq ptr %p, @a
call void @llvm.assume(i1 %1)
%2 = load i32, ptr %p, align 4
ret i32 %2
}
```

They optimize above to return `undef`, which is inconsistent with a situation where constants like `@a` and `@b` have the same address and `@f` is called with a pointer to `@b`.

```llvm
define noundef i32 @f(ptr align 4 %p) {
%1 = icmp eq ptr %p, @a
call void @llvm.assume(i1 %1)
ret i32 undef
}
```

Related issues: https://github.com/rust-lang/rust/issues/161973, https://github.com/rust-lang/rust/issues/162078

Contributor guide

Open the contributing guide

Research direction

Start by running the supplied LLVM IR with clang-24 and reproducing the address comparison result. Then inspect the assumptions described for GVN and SCCP alongside linker merging of @a and @b. Done means the semantics of unnamed_addr, optimizer reasoning, and generated behavior agree for initialized and uninitialized bytes.

Written by the indexing model from the issue text.

Assessment

Domain
compilers
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.