Skip to content

Commit ca0eb76

Browse files
author
asdadqwe
committed
Add AMDGPU miscompile reproducer m015
1 parent a48f5bb commit ca0eb76

4 files changed

Lines changed: 110 additions & 1 deletion

File tree

‎amdgpu/README.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ rediscovering the same issue.
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). |
7373
| `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). |
74+
| `FUZZX_ALLOW_M015_SCALAR_FSHL_ZERO=1` | unset | Re-enable zero-count `fshl` generation for [m015](known-miscompiles/m015-scalar-fshl-zero/NOTES.md). |
7475

7576
## Layout
7677

@@ -107,6 +108,7 @@ Version | Description |
107108
| 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`. |
108109
| 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. |
109110
| 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. |
111+
| LLVM 23.0.0git | [m015-scalar-fshl-zero](known-miscompiles/m015-scalar-fshl-zero/NOTES.md): `-O0` lowers scalar `fshl.i32(x, y, 0)` through a 64-bit shift-by-`-1` sequence that returns zero. |
110112

111113
## LLVM Source Builds
112114

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

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,24 @@ ConstantInt *ci64(LLVMContext &Ctx, uint64_t V) {
402402
return ConstantInt::get(Type::getInt64Ty(Ctx), V);
403403
}
404404

405+
bool allowM015ScalarFshlZero() {
406+
static const bool Allow =
407+
envFlag("FUZZX_ALLOW_M015_SCALAR_FSHL_ZERO", false);
408+
return Allow;
409+
}
410+
411+
Value *suppressM015FshlZeroShift(IRBuilder<> &B, Value *Shift) {
412+
if (allowM015ScalarFshlZero())
413+
return Shift;
414+
415+
LLVMContext &Ctx = B.getContext();
416+
if (auto *CI = dyn_cast<ConstantInt>(Shift))
417+
return CI->isZero() ? ci32(Ctx, 1) : Shift;
418+
419+
Value *IsZero = B.CreateICmpEQ(Shift, ci32(Ctx, 0));
420+
return B.CreateSelect(IsZero, ci32(Ctx, 1), Shift);
421+
}
422+
405423
Constant *vectorConstant(LLVMContext &Ctx, ArrayRef<uint32_t> Values,
406424
bool ShiftAmounts = false) {
407425
SmallVector<Constant *, 4> Constants;
@@ -690,6 +708,8 @@ Value *emitRotate(IRBuilder<> &B, Module &M, Value *V, const Op &O,
690708
LLVMContext &Ctx = B.getContext();
691709
Type *I32 = Type::getInt32Ty(Ctx);
692710
Value *Shift = B.CreateAnd(B.CreateXor(V, ci32(Ctx, u32(O.B))), ci32(Ctx, 31));
711+
if (ID == Intrinsic::fshl)
712+
Shift = suppressM015FshlZeroShift(B, Shift);
693713
return B.CreateCall(Intrinsic::getOrInsertDeclaration(&M, ID, {I32}),
694714
{V, ci32(Ctx, u32(O.A)), Shift});
695715
}
@@ -810,7 +830,8 @@ Value *emitOp(IRBuilder<> &B, Module &M, Value *V, Value *Idx, const Op &O) {
810830
case 29:
811831
return B.CreateCall(Intrinsic::getOrInsertDeclaration(&M, Intrinsic::fshl, {I32}),
812832
{V, ci32(Ctx, u32(O.A)),
813-
ci32(Ctx, static_cast<uint32_t>(O.B & 31u))});
833+
suppressM015FshlZeroShift(
834+
B, ci32(Ctx, static_cast<uint32_t>(O.B & 31u)))});
814835
case 30:
815836
return B.CreateCall(Intrinsic::getOrInsertDeclaration(&M, Intrinsic::fshr, {I32}),
816837
{ci32(Ctx, u32(O.A)), V,
@@ -844,6 +865,7 @@ Value *emitOp(IRBuilder<> &B, Module &M, Value *V, Value *Idx, const Op &O) {
844865
{V, ConstantInt::getFalse(Ctx)});
845866
case 42: {
846867
Value *Shift = B.CreateAnd(V, ci32(Ctx, 31));
868+
Shift = suppressM015FshlZeroShift(B, Shift);
847869
return B.CreateCall(Intrinsic::getOrInsertDeclaration(&M, Intrinsic::fshl, {I32}),
848870
{V, ci32(Ctx, u32(O.A)), Shift});
849871
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# m015: scalar `fshl` by zero returns zero at `-O0`
2+
3+
## Reproducer
4+
5+
```bash
6+
amdgpu/known-miscompiles/run_ll_reproducer.sh \
7+
amdgpu/known-miscompiles/m015-scalar-fshl-zero/reduced.ll
8+
```
9+
10+
With the instrumented LLVM build at `amdgpu/build/llvm-fuzzer`, the reduced
11+
testcase produces:
12+
13+
```text
14+
input=0x00000003
15+
O0=0x00000000
16+
O2=0x00000003
17+
mismatch=true
18+
```
19+
20+
ROCm 7.1.1 does not reproduce this mismatch; both optimization levels return
21+
`0x00000003`.
22+
23+
## Root Cause Notes
24+
25+
The original fuzzer finding was
26+
`amdgpu/findings/cxx-diff-1778937697-1007403`. The first divergent value was an
27+
`llvm.fshl.i32` with a constant zero shift amount. At `-O2`, the operation is
28+
simplified to the left input, which is the defined result for
29+
`llvm.fshl.i32(x, y, 0)`.
30+
31+
At `-O0`, AMDGPU scalarizes the left input into SGPRs and lowers the zero-count
32+
`fshl` through a 64-bit shift sequence. The generated scalar sequence computes a
33+
second shift count of `-1`:
34+
35+
```asm
36+
s_lshr_b32 s4, s6, 1
37+
s_mov_b32 s0, 0x5040305
38+
s_mov_b32 s1, s6
39+
s_lshr_b64 s[0:1], s[0:1], 1
40+
s_mov_b32 s1, s4
41+
s_mov_b32 s4, -1
42+
s_lshr_b64 s[0:1], s[0:1], s4
43+
```
44+
45+
That final `s_lshr_b64` by `-1` zeros the value in this reproducer. A direct
46+
VGPR-source `fshl(load, const, 0)` matches, and `fshr(const, scalar, 0)` also
47+
matches, so the suppression is limited to generated `fshl` zero-count shapes.
48+
49+
## Fuzzer Suppression
50+
51+
The directed C++ fuzzer avoids generating zero-count `llvm.fshl.i32` by default.
52+
Set `FUZZX_ALLOW_M015_SCALAR_FSHL_ZERO=1` to re-enable this bug class.
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
; RUN-INPUTS: 3
2+
; RUN-LLVM-BUILD: build/llvm-fuzzer
3+
target triple = "amdgcn-amd-amdhsa"
4+
5+
define protected amdgpu_kernel void @fuzz_kernel(ptr addrspace(1) %in, ptr addrspace(1) %out, i32 %n) #0 {
6+
entry:
7+
%wg = call i32 @llvm.amdgcn.workgroup.id.x()
8+
%wi = call i32 @llvm.amdgcn.workitem.id.x()
9+
%base = mul i32 %wg, 256
10+
%idx32 = add i32 %base, %wi
11+
%active = icmp ult i32 %idx32, %n
12+
br i1 %active, label %body, label %exit
13+
14+
body:
15+
%idx = zext i32 %idx32 to i64
16+
%inptr = getelementptr i32, ptr addrspace(1) %in, i64 %idx
17+
%loaded = load i32, ptr addrspace(1) %inptr, align 4
18+
%scalar = call i32 @llvm.amdgcn.readfirstlane(i32 %loaded)
19+
%result = call i32 @llvm.fshl.i32(i32 %scalar, i32 84148997, i32 0)
20+
%outptr = getelementptr i32, ptr addrspace(1) %out, i64 %idx
21+
store i32 %result, ptr addrspace(1) %outptr, align 4
22+
br label %exit
23+
24+
exit:
25+
ret void
26+
}
27+
28+
declare noundef i32 @llvm.amdgcn.workgroup.id.x()
29+
declare noundef range(i32 0, 1024) i32 @llvm.amdgcn.workitem.id.x()
30+
declare i32 @llvm.amdgcn.readfirstlane(i32)
31+
declare i32 @llvm.fshl.i32(i32, i32, i32)
32+
33+
attributes #0 = { convergent nounwind "amdgpu-flat-work-group-size"="1,256" "target-cpu"="gfx950" "uniform-work-group-size"="true" }

0 commit comments

Comments
 (0)