Skip to content

Commit 91cdd00

Browse files
author
asdadqwe
committed
Add AMDGPU miscompile reproducer m014
1 parent b6fc9c9 commit 91cdd00

4 files changed

Lines changed: 137 additions & 2 deletions

File tree

‎amdgpu/README.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ rediscovering the same issue.
7070
| `FUZZX_ALLOW_M011_I8_SEXT_CLEAR_XOR=1` | unset | Re-enable the `i8` sign-extended identity clear xor shape for [m011](known-miscompiles/m011-i8-sext-clear-xor/NOTES.md). |
7171
| `FUZZX_ALLOW_M012_ADD_SHL_LADDER=1` | unset | Alias to re-enable the broader `add/shl` ladder shape for [m012](known-miscompiles/m012-add-shl-ladder/NOTES.md). |
7272
| `FUZZX_ALLOW_M013_PRIVATE_MEMORY_FSHL=1` | unset | Re-enable three-or-more private-memory/funnel-shift ops for [m013](known-miscompiles/m013-private-memory-fshl/NOTES.md). |
73+
| `FUZZX_ALLOW_M014_SHL_ADD_CTPOP=1` | unset | Re-enable four-step `shl/add` chains feeding `ctpop` for [m014](known-miscompiles/m014-shl-add-ctpop/NOTES.md). |
7374

7475
## Layout
7576

@@ -105,6 +106,7 @@ Version | Description |
105106
| LLVM 23.0.0git | [m011-i8-sext-clear-xor](known-miscompiles/m011-i8-sext-clear-xor/NOTES.md): `-O0` miscompiles an `i8` sign-extended masked clear xor through the wrong `v_bitop3_b32` result. |
106107
| LLVM 23.0.0git | [m012-add-shl-ladder](known-miscompiles/m012-add-shl-ladder/NOTES.md): `-O0` scalarizes a divergent `add/shl` ladder through `v_readfirstlane_b32`. |
107108
| ROCm 7.1.1 / LLVM 23.0.0git | [m013-private-memory-fshl](known-miscompiles/m013-private-memory-fshl/NOTES.md): `-O0` lowers fixed private-memory allocas through a dynamic scratch stack sequence that can return intermittent wrong values. |
109+
| LLVM 23.0.0git | [m014-shl-add-ctpop](known-miscompiles/m014-shl-add-ctpop/NOTES.md): `-O0` scalarizes a four-step `shl/add` chain feeding `ctpop` through lane 0. |
108110

109111
## LLVM Source Builds
110112

‎amdgpu/fuzzers/llvm-amdgpu-diff/llvm_amdgpu_diff_fuzzer.cpp‎

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -203,15 +203,15 @@ bool isI32AddZero(const Op &O) { return isI32Add(O) && u32(O.A) == 0; }
203203

204204
bool isI32ShlZero(const Op &O) { return isI32Shl(O) && (O.A & 31u) == 0; }
205205

206-
bool hasFiveShlAddPairs(ArrayRef<Op> Ops) {
206+
bool hasShlAddPairs(ArrayRef<Op> Ops, unsigned RequiredPairs) {
207207
unsigned Pairs = 0;
208208
bool NeedAdd = false;
209209
for (const Op &O : Ops) {
210210
if (NeedAdd) {
211211
if (isI32Add(O)) {
212212
++Pairs;
213213
NeedAdd = false;
214-
if (Pairs >= 5)
214+
if (Pairs >= RequiredPairs)
215215
return true;
216216
continue;
217217
}
@@ -229,6 +229,13 @@ bool hasFiveShlAddPairs(ArrayRef<Op> Ops) {
229229
return false;
230230
}
231231

232+
bool hasFiveShlAddPairs(ArrayRef<Op> Ops) { return hasShlAddPairs(Ops, 5); }
233+
234+
bool hasFourShlAddPairsBeforeCtpop(ArrayRef<Op> Ops) {
235+
return !Ops.empty() && Ops.back().Kind == 11 &&
236+
hasShlAddPairs(Ops.drop_back(), 4);
237+
}
238+
232239
bool hasAddShlLadder(ArrayRef<Op> Ops) {
233240
unsigned Pairs = 0;
234241
bool NeedShl = false;
@@ -322,6 +329,7 @@ Program makeProgram(const uint8_t *Data, size_t Size) {
322329
bool AllowM003 = envFlag("FUZZX_ALLOW_M003_SHL3_ADD_CHAIN", false) ||
323330
envFlag("FUZZX_ALLOW_M005_SHL_ADD_CHAIN", false) ||
324331
envFlag("FUZZX_ALLOW_M012_ADD_SHL_LADDER", false);
332+
bool AllowM014 = envFlag("FUZZX_ALLOW_M014_SHL_ADD_CTPOP", false);
325333
bool AllowM004 = envFlag("FUZZX_ALLOW_M004_VECTOR_IDENTITY_XOR", false) ||
326334
envFlag("FUZZX_ALLOW_M007_VECTOR_IDENTITY_XOR", false);
327335
bool AllowM013 = envFlag("FUZZX_ALLOW_M013_PRIVATE_MEMORY_FSHL", false);
@@ -355,6 +363,8 @@ Program makeProgram(const uint8_t *Data, size_t Size) {
355363
P.Ops.back().Kind = 11;
356364
}
357365
}
366+
if (!AllowM014 && hasFourShlAddPairsBeforeCtpop(P.Ops))
367+
P.Ops.back().Kind = 0;
358368
if (!AllowM013 && hasThreePrivateMemoryOps(P.Ops))
359369
P.Ops.back().Kind = 54;
360370
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
# m014: shl/add chain feeding ctpop scalarized through lane 0
2+
3+
## Summary
4+
5+
Upstream AMDGPU `-O0` miscompiles a divergent four-step `shl`/`add` chain when
6+
the result feeds `llvm.ctpop.i32`:
7+
8+
```llvm
9+
%t0 = shl i32 %x, 1
10+
%t1 = add i32 %t0, 1
11+
%t2 = shl i32 %t1, 1
12+
%t3 = add i32 %t2, 1
13+
%t4 = shl i32 %t3, 1
14+
%t5 = add i32 %t4, 1
15+
%t6 = shl i32 %t5, 1
16+
%t7 = add i32 %t6, 1
17+
%result = call i32 @llvm.ctpop.i32(i32 %t7)
18+
```
19+
20+
For inputs `[0, 1]`, lane 0 should return `4` and lane 1 should return `5`.
21+
The affected `-O0` compile returns `4` for both lanes.
22+
23+
## Reproduce
24+
25+
From `amdgpu/`:
26+
27+
```bash
28+
known-miscompiles/run_ll_reproducer.sh \
29+
known-miscompiles/m014-shl-add-ctpop/reduced.ll
30+
```
31+
32+
Expected output on the affected toolchain:
33+
34+
```text
35+
[0] input=0x00000000 O0=0x00000004 O2=0x00000004 mismatch=false
36+
[1] input=0x00000001 O0=0x00000004 O2=0x00000005 mismatch=true
37+
any_mismatch=true
38+
```
39+
40+
## Root Cause
41+
42+
The IR is fully defined. All arithmetic uses wrapping semantics, all shifts use
43+
constant in-range amounts, and there are no `undef`, `poison`, `nuw`, `nsw`,
44+
`exact`, `inbounds`, or division operations.
45+
46+
The affected `-O0` lowering reads only one divergent lane before evaluating the
47+
chain:
48+
49+
```text
50+
v_readfirstlane_b32 s2, v2
51+
s_lshl1_add_u32 s2, s2, s3
52+
s_lshl1_add_u32 s2, s2, s3
53+
s_lshl1_add_u32 s2, s2, s3
54+
s_lshl1_add_u32 s2, s2, s3
55+
v_bcnt_u32_b32 v2, s2, 0
56+
```
57+
58+
That computes `ctpop((((lane0 << 1) + 1) ...))` and broadcasts the result to all
59+
active lanes. This is the same broad scalarization family as m003, m005, and
60+
m012, but the four-step chain only miscompiled once its result fed `ctpop`.
61+
62+
## Checked Toolchains
63+
64+
Checked on 2026-05-16 on `gfx950`.
65+
66+
| Toolchain | Result |
67+
| --- | --- |
68+
| Upstream LLVM 23.0.0git, commit `a1403139d0ba7fdfc82d6ae8a2884f27fec9fa15`, built with sanitizer coverage | Reproduces. |
69+
| ROCm 7.1.1 clang 20.0.0git, commit `27682a16360e33e37c4f3cc6adf9a620733f8fe1` | Does not reproduce this reduced case. |
70+
71+
Original fuzzer input SHA-1 values:
72+
73+
```text
74+
1d3be17a5edcc02ab532c9bc7349a9216d8795ba
75+
622126ebaa826b2b978babd12eb70a5b9de8d0c5
76+
```
77+
78+
## Fuzzer Suppression
79+
80+
The directed C++ fuzzer now suppresses four-step `shl/add` chains immediately
81+
before `ctpop`. Set `FUZZX_ALLOW_M014_SHL_ADD_CTPOP=1` to re-enable this class
82+
when replaying old fuzzer inputs.
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
; RUN-INPUTS: 0, 1
2+
; RUN-LLVM-BUILD: build/llvm-fuzzer
3+
source_filename = "m014-shl-add-ctpop"
4+
target datalayout = "e-m:e-p:64:64-p1:64:64-p2:32:32-p3:32:32-p4:64:64-p5:32:32-p6:32:32-p7:160:256:256:32-p8:128:128:128:48-p9:192:256:256:32-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-v2048:2048-n32:64-S32-A5-G1-ni:7:8:9"
5+
target triple = "amdgcn-amd-amdhsa"
6+
7+
define protected amdgpu_kernel void @fuzz_kernel(ptr addrspace(1) %in, ptr addrspace(1) %out, i32 %n) #0 {
8+
entry:
9+
%wg = call i32 @llvm.amdgcn.workgroup.id.x()
10+
%wi = call i32 @llvm.amdgcn.workitem.id.x()
11+
%base = mul i32 %wg, 256
12+
%idx = add i32 %base, %wi
13+
%ok = icmp ult i32 %idx, %n
14+
br i1 %ok, label %body, label %exit
15+
16+
body:
17+
%idx64 = zext i32 %idx to i64
18+
%inptr = getelementptr i32, ptr addrspace(1) %in, i64 %idx64
19+
%x = load i32, ptr addrspace(1) %inptr, align 4
20+
%t0 = shl i32 %x, 1
21+
%t1 = add i32 %t0, 1
22+
%t2 = shl i32 %t1, 1
23+
%t3 = add i32 %t2, 1
24+
%t4 = shl i32 %t3, 1
25+
%t5 = add i32 %t4, 1
26+
%t6 = shl i32 %t5, 1
27+
%t7 = add i32 %t6, 1
28+
%result = call i32 @llvm.ctpop.i32(i32 %t7)
29+
%outptr = getelementptr i32, ptr addrspace(1) %out, i64 %idx64
30+
store i32 %result, ptr addrspace(1) %outptr, align 4
31+
br label %exit
32+
33+
exit:
34+
ret void
35+
}
36+
37+
declare i32 @llvm.amdgcn.workgroup.id.x()
38+
declare i32 @llvm.amdgcn.workitem.id.x()
39+
declare i32 @llvm.ctpop.i32(i32)
40+
41+
attributes #0 = { convergent nounwind "amdgpu-flat-work-group-size"="1,256" "target-cpu"="gfx950" "uniform-work-group-size"="true" }

0 commit comments

Comments
 (0)