Skip to content

Commit 8c5c028

Browse files
jlebarclaude
andcommitted
x86: +6 reproducible default-O2 bugs #208-#213
#208 SDAG memcpy lowering drops !nontemporal #209 SDAG memmove lowering drops !nontemporal #210 SDAG memset lowering drops !nontemporal #211 InstCombine unpackLoadToAggregate drops !nontemporal #212 InstCombine unpackStoreToAggregate drops !nontemporal #213 LegalizeIntegerTypes ExpandIntRes_LOAD drops !range on i128 split Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 20f7b97 commit 8c5c028

46 files changed

Lines changed: 3013 additions & 0 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎x86/README.md‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,3 +241,9 @@ Goal: find ≥100 real bugs in the x86 path through the default LLVM pass pipeli
241241
| 205 | [205-scalarize-masked-compressstore-drops-nontemporal](bugs/205-scalarize-masked-compressstore-drops-nontemporal/) | ScalarizeMaskedMemIntrin scalarizeMaskedCompressStore | mirror of #204 — per-lane stores drop NT/AAMD on both const- and dyn-mask paths | confirmed (opt diff) |
242242
| 206 | [206-simplifylibcalls-fmod-incorrect-nnan-on-frem](bugs/206-simplifylibcalls-fmod-incorrect-nnan-on-frem/) | SimplifyLibCalls optimizeFMod | `fmod(NaN, 1.0)` folded to `frem nnan NaN, 1.0` → **poison**; `IsNoNan` proof actually checks no-errno not no-NaN | confirmed (opt diff) |
243243
| 207 | [207-simplifylibcalls-fdim-inf-minus-inf-qnan](bugs/207-simplifylibcalls-fdim-inf-minus-inf-qnan/) | SimplifyLibCalls optimizeFdim | `fdim(±Inf, ±Inf)` folds to qNaN instead of `+0.0` per C99 (uses `max(X-Y, 0)` instead of comparison-first definition) | confirmed (opt diff) |
244+
| 208 | [208-sdag-memcpy-lowering-drops-nontemporal](bugs/208-sdag-memcpy-lowering-drops-nontemporal/) | SelectionDAG getMemcpyLoadsAndStores | `llvm.memcpy ..., !nontemporal` → per-chunk MMOs with no MONonTemporal; x86 emits cached MOV* instead of MOVNT* | confirmed (mir diff) |
245+
| 209 | [209-sdag-memmove-lowering-drops-nontemporal](bugs/209-sdag-memmove-lowering-drops-nontemporal/) | SelectionDAG getMemmoveLoadsAndStores | sister of #208 for memmove | confirmed (mir diff) |
246+
| 210 | [210-sdag-memset-lowering-drops-nontemporal](bugs/210-sdag-memset-lowering-drops-nontemporal/) | SelectionDAG getMemsetStores | sister of #208 for memset | confirmed (mir diff) |
247+
| 211 | [211-instcombine-unpack-struct-load-drops-nontemporal](bugs/211-instcombine-unpack-struct-load-drops-nontemporal/) | InstCombine unpackLoadToAggregate | per-field `load i32` instructions don't inherit `!nontemporal`/`!access_group` from aggregate load | confirmed (opt diff) |
248+
| 212 | [212-instcombine-unpack-struct-store-drops-nontemporal](bugs/212-instcombine-unpack-struct-store-drops-nontemporal/) | InstCombine unpackStoreToAggregate | mirror of #211 for stores | confirmed (opt diff) |
249+
| 213 | [213-legalize-expandintres-load-drops-range](bugs/213-legalize-expandintres-load-drops-range/) | LegalizeIntegerTypes ExpandIntRes_LOAD | i128 load split into two i64 loads drops `!range` on both MMOs | confirmed (mir diff) |
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# 208 — SelectionDAG `getMemcpyLoadsAndStores` drops `!nontemporal` from `llvm.memcpy`
2+
3+
Component: `llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp` lines ~9331-9332 (helper), with `SelectionDAGBuilder::visitMemCpyInst` (line 6711) failing to query `MD_nontemporal` and pass it through.
4+
5+
When the SDAG inline-expansion lowers an `llvm.memcpy` annotated `!nontemporal !0`, the per-chunk loads/stores receive MMOFlags computed only from `isVol`. MONonTemporal is silently dropped, so the x86 selector emits cached `VMOVUPS*`/`MOVAPS*` instead of `VMOVNTPSmr`/`MOVNTDQmr`. The wiring works for direct IR stores `store …, !nontemporal` — only the memcpy-lowering helper drops the bit.
6+
7+
## Reproducer
8+
9+
`llc -O2 -mtriple=x86_64-unknown-linux-gnu -mattr=+avx2 -stop-after=finalize-isel repro.ll -o -`
10+
11+
Per the MIR: each `VMOVUPSYrm/mr` MMO is plain `(load/store (s256) from %ir.s, align 16)` — no `non-temporal` flag. Expected: each MMO should carry `non-temporal load/store`.
12+
13+
## Severity
14+
15+
Default x86 -O2. NT hint on memcpy intrinsics is the only sane way for source-language `__builtin_memcpy_nontemporal` to reach the MOVNT path; this drops it entirely.
16+
17+
## Fix
18+
19+
Plumb `MD_nontemporal` (and MOInvariant where applicable) from the call instruction into `getMemcpyLoadsAndStores` via an extra `MachineMemOperand::Flags` parameter.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#!/usr/bin/env bash
2+
LLC=/home/orenamd@semianalysis.com/FuzzX/amdgpu/build/llvm-fuzzer/bin/llc
3+
"$LLC" -O2 -mtriple=x86_64-unknown-linux-gnu -mattr=+avx2 -stop-after=finalize-isel repro.ll -o - 2>&1 | grep -E "VMOV|MMO|non-temporal" | head
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
target triple = "x86_64-unknown-linux-gnu"
2+
declare void @llvm.memcpy.p0.p0.i64(ptr, ptr, i64, i1)
3+
define void @f(ptr %d, ptr %s) {
4+
call void @llvm.memcpy.p0.p0.i64(ptr align 16 %d, ptr align 16 %s, i64 64, i1 false), !nontemporal !0
5+
ret void
6+
}
7+
!0 = !{i32 1}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# 209 — SelectionDAG `getMemmoveLoadsAndStores` drops `!nontemporal` from `llvm.memmove`
2+
3+
Component: `llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp` lines ~9520-9521; sibling of #208.
4+
5+
Same defect as #208 in the memmove path. Per-chunk loads/stores never receive the MONonTemporal flag set on the original intrinsic call.
6+
7+
## Reproducer / fix: same shape as #208.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#!/usr/bin/env bash
2+
LLC=/home/orenamd@semianalysis.com/FuzzX/amdgpu/build/llvm-fuzzer/bin/llc
3+
"$LLC" -O2 -mtriple=x86_64-unknown-linux-gnu -mattr=+avx2 -stop-after=finalize-isel repro.ll -o - 2>&1 | grep -E "VMOV|MMO|non-temporal" | head
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
target triple = "x86_64-unknown-linux-gnu"
2+
declare void @llvm.memmove.p0.p0.i64(ptr, ptr, i64, i1)
3+
define void @f(ptr %d, ptr %s) {
4+
call void @llvm.memmove.p0.p0.i64(ptr align 16 %d, ptr align 16 %s, i64 64, i1 false), !nontemporal !0
5+
ret void
6+
}
7+
!0 = !{i32 1}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# 210 — SelectionDAG `getMemsetStores` drops `!nontemporal` from `llvm.memset`
2+
3+
Component: `llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp` lines ~9747-9751; sibling of #208/#209.
4+
5+
Per-chunk stores never receive MONonTemporal from the original intrinsic. The X86 selector therefore emits plain `MOVAPSmr`/`MOV*mr` instead of NT-class stores even when the source said `__builtin_memset_inline + nontemporal`.
6+
7+
## Reproducer / fix: same shape as #208.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#!/usr/bin/env bash
2+
LLC=/home/orenamd@semianalysis.com/FuzzX/amdgpu/build/llvm-fuzzer/bin/llc
3+
"$LLC" -O2 -mtriple=x86_64-unknown-linux-gnu -mattr=+avx2 -stop-after=finalize-isel repro.ll -o - 2>&1 | grep -E "VMOV|MMO|non-temporal" | head
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
target triple = "x86_64-unknown-linux-gnu"
2+
declare void @llvm.memset.p0.i64(ptr, i8, i64, i1)
3+
define void @f(ptr %d) {
4+
call void @llvm.memset.p0.i64(ptr align 16 %d, i8 0, i64 64, i1 false), !nontemporal !0
5+
ret void
6+
}
7+
!0 = !{i32 1}

0 commit comments

Comments
 (0)