Skip to content

Commit bfecc8e

Browse files
committed
Add latest ptxas miscompile reproducers
1 parent d4cc1f3 commit bfecc8e

19 files changed

Lines changed: 1010 additions & 171 deletions

File tree

‎README.md‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,10 @@ Version | Description |
114114
| 13.2.78 | [m035-xor-not-predicate-fold](known-miscompiles/m035-xor-not-predicate-fold/NOTES.md): Likely same root cause as m033; `xor.b32` by `0xffffffff` feeding a predicate selects the wrong arm. |
115115
| 13.2.78 | [m034-bfind-zero-branch-fold](known-miscompiles/m034-bfind-zero-branch-fold/NOTES.md): Branch fold treats `bfind.u32 0` as `0` instead of `0xffffffff`. |
116116
| 13.2.78 | [m036-mulhi-control-fold](known-miscompiles/m036-mulhi-control-fold/NOTES.md): Control-flow fold around `mul.hi.s32` uses an incorrect folded constant. |
117+
| 13.2.78 | [m037-bmsk-clz-bfi-fold](known-miscompiles/m037-bmsk-clz-bfi-fold/NOTES.md): `bmsk` / `clz` / `bfi` / `mad.lo` value-chain fold sets an extra output bit. |
118+
| 13.2.78 | [m038-structured-empty-else-fold](known-miscompiles/m038-structured-empty-else-fold/NOTES.md): Always-false structured branch with an empty else arm folds as if the untaken then arm executed. |
119+
| 13.2.78 | [m039-else-redefinition-fold](known-miscompiles/m039-else-redefinition-fold/NOTES.md): Branch fold drops the executed else-path redefinition of a value initialized before the branch. |
120+
| 13.2.78 | [m040-mulwide-neg-shr-fold](known-miscompiles/m040-mulwide-neg-shr-fold/NOTES.md): `mul.wide` low word feeding wrapped negation and logical shift loses the shifted high-bit contribution. |
117121

118122
## Running
119123

@@ -205,6 +209,7 @@ that feature.
205209
| `DIV_DISABLE_MULHI` | `mul.hi.u32` and `mul.hi.s32`. |
206210
| `DIV_DISABLE_SIGNED_MULHI` | `mul.hi.s32` only. |
207211
| `DIV_DISABLE_BITWISE_BINOPS` | `and.b32`, `or.b32`, `xor.b32`. |
212+
| `DIV_DISABLE_XOR` | `xor.b32` while retaining `and.b32` and `or.b32`. |
208213
| `DIV_DISABLE_PRMT` | `prmt.b32`. |
209214
| `DIV_DISABLE_NOT` | `not.b32` and xor-by-`0xffffffff` forms. |
210215
| `DIV_DISABLE_CLZ` | `clz.b32`. |

‎crates/fuzzx-diff/src/bin/reduce.rs‎

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -260,17 +260,30 @@ fn find_greedy_removal(
260260
}
261261

262262
/// Indices into `ptx.lines()` that the reducer is allowed to try removing.
263-
/// Sacrosanct: prologue (everything up to and including `bra block_0;`),
264-
/// epilogue address arithmetic, labels, structural lines, `ret;`, braces.
263+
/// Sacrosanct: prologue (everything through the generated ABI/register
264+
/// initialization), epilogue address arithmetic, labels, structural lines,
265+
/// `ret;`, braces.
265266
fn removable_indices(ptx: &str) -> Result<Vec<usize>> {
266267
let lines: Vec<&str> = ptx.lines().collect();
267-
let prologue_end = lines
268+
let prologue_end = if let Some(i) = lines
268269
.iter()
269270
.position(|l| {
270271
let t = l.trim();
271272
t.starts_with("bra") && t.contains("block_0;")
272-
})
273-
.ok_or_else(|| anyhow!("could not locate `bra block_0;` to mark end of prologue"))?;
273+
}) {
274+
i
275+
} else {
276+
lines
277+
.iter()
278+
.enumerate()
279+
.skip_while(|(_, l)| !l.trim().starts_with("ld.global.u32"))
280+
.find_map(|(i, l)| (i > 0 && l.trim().is_empty()).then_some(i))
281+
.ok_or_else(|| {
282+
anyhow!(
283+
"could not locate `bra block_0;` or structured register-init end to mark end of prologue"
284+
)
285+
})?
286+
};
274287
let exit_start = lines
275288
.iter()
276289
.position(|l| l.trim() == "exit:")
@@ -283,6 +296,11 @@ fn removable_indices(ptx: &str) -> Result<Vec<usize>> {
283296
if t.is_empty() || t.ends_with(':') {
284297
continue;
285298
}
299+
// Removing a predicate definition can leave an undefined branch or
300+
// selp predicate while still producing deterministic-looking output.
301+
if t.starts_with("setp.") {
302+
continue;
303+
}
286304
out.push(i);
287305
}
288306
// Epilogue: store lines only; address arithmetic and `ret;` stay put.

‎crates/fuzzx-diff/src/main.rs‎

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@
3232
//! PTX mul.hi.s32 generation while retaining mul.hi.u32
3333
//! DIV_DISABLE_BITWISE_BINOPS default: false; set 1/true/yes/on to suppress
3434
//! PTX and.b32/or.b32/xor.b32 generation
35+
//! DIV_DISABLE_XOR default: false; set 1/true/yes/on to suppress
36+
//! PTX xor.b32 generation while retaining and.b32/or.b32
3537
//! DIV_DISABLE_PRMT default: false; set 1/true/yes/on to suppress
3638
//! PTX prmt.b32 generation
3739
//! DIV_DISABLE_NOT default: false; set 1/true/yes/on to suppress
@@ -189,6 +191,8 @@ struct Args {
189191
#[arg(long)]
190192
disable_bitwise_binops: bool,
191193
#[arg(long)]
194+
disable_xor: bool,
195+
#[arg(long)]
192196
disable_prmt: bool,
193197
#[arg(long)]
194198
disable_not: bool,
@@ -289,6 +293,7 @@ impl Args {
289293
set_bool!(self.disable_mulhi, "DIV_DISABLE_MULHI");
290294
set_bool!(self.disable_signed_mulhi, "DIV_DISABLE_SIGNED_MULHI");
291295
set_bool!(self.disable_bitwise_binops, "DIV_DISABLE_BITWISE_BINOPS");
296+
set_bool!(self.disable_xor, "DIV_DISABLE_XOR");
292297
set_bool!(self.disable_prmt, "DIV_DISABLE_PRMT");
293298
set_bool!(self.disable_not, "DIV_DISABLE_NOT");
294299
set_bool!(self.disable_clz, "DIV_DISABLE_CLZ");
@@ -389,6 +394,7 @@ impl Config {
389394
let disable_mulhi = env_bool("DIV_DISABLE_MULHI")?.unwrap_or(false);
390395
let disable_signed_mulhi = env_bool("DIV_DISABLE_SIGNED_MULHI")?.unwrap_or(false);
391396
let disable_bitwise_binops = env_bool("DIV_DISABLE_BITWISE_BINOPS")?.unwrap_or(false);
397+
let disable_xor = env_bool("DIV_DISABLE_XOR")?.unwrap_or(false);
392398
let disable_prmt = env_bool("DIV_DISABLE_PRMT")?.unwrap_or(false);
393399
let disable_not = env_bool("DIV_DISABLE_NOT")?.unwrap_or(false);
394400
let disable_clz = env_bool("DIV_DISABLE_CLZ")?.unwrap_or(false);
@@ -429,6 +435,7 @@ impl Config {
429435
emit_mulhi: !disable_mulhi,
430436
emit_signed_mulhi: !disable_signed_mulhi,
431437
emit_bitwise_binops: !disable_bitwise_binops,
438+
emit_xor: !disable_xor,
432439
emit_prmt: !disable_prmt,
433440
emit_not: !disable_not,
434441
emit_clz: !disable_clz,
@@ -639,7 +646,7 @@ fn main() -> Result<()> {
639646

640647
let total_workers = cfg.gpus.len() * cfg.workers_per_gpu;
641648
eprintln!(
642-
"fuzzx-diff: starting_seed=0x{:016x} out={} program_bytes={} max_iters={} control_flow={:?} blocks={}..{} insts_per_block={}..{} regs={} max_loop_iters={} max_immediate={} max_structured_depth={} emit_structured_loops={} emit_arbitrary_loops={} emit_lop3={} emit_minmax={} emit_sub={} emit_mulhi={} emit_signed_mulhi={} emit_bitwise_binops={} emit_prmt={} emit_not={} emit_clz={} emit_cnot={} emit_abs={} emit_signed_cmp={} emit_signed_divrem={} emit_funnel={} emit_neg={} emit_shl={} emit_signed_shr={} emit_bfind={} emit_bfi={} emit_bmsk={} emit_mad24={} emit_mul24={} emit_mul_wide={} emit_wide_int={} emit_addc={} emit_subc={} emit_i32_boundary_immediates={} emit_dp2a={} emit_set={} emit_s32_slct={} emit_video={} emit_vsub4={} gpus={:?} workers_per_gpu={} (total={})",
649+
"fuzzx-diff: starting_seed=0x{:016x} out={} program_bytes={} max_iters={} control_flow={:?} blocks={}..{} insts_per_block={}..{} regs={} max_loop_iters={} max_immediate={} max_structured_depth={} emit_structured_loops={} emit_arbitrary_loops={} emit_lop3={} emit_minmax={} emit_sub={} emit_mulhi={} emit_signed_mulhi={} emit_bitwise_binops={} emit_xor={} emit_prmt={} emit_not={} emit_clz={} emit_cnot={} emit_abs={} emit_signed_cmp={} emit_signed_divrem={} emit_funnel={} emit_neg={} emit_shl={} emit_signed_shr={} emit_bfind={} emit_bfi={} emit_bmsk={} emit_mad24={} emit_mul24={} emit_mul_wide={} emit_wide_int={} emit_addc={} emit_subc={} emit_i32_boundary_immediates={} emit_dp2a={} emit_set={} emit_s32_slct={} emit_video={} emit_vsub4={} gpus={:?} workers_per_gpu={} (total={})",
643650
cfg.starting_seed,
644651
cfg.out_dir.display(),
645652
cfg.program_bytes,
@@ -663,6 +670,7 @@ fn main() -> Result<()> {
663670
cfg.gen_config.emit_mulhi,
664671
cfg.gen_config.emit_signed_mulhi,
665672
cfg.gen_config.emit_bitwise_binops,
673+
cfg.gen_config.emit_xor,
666674
cfg.gen_config.emit_prmt,
667675
cfg.gen_config.emit_not,
668676
cfg.gen_config.emit_clz,

‎crates/fuzzx-execgen/src/lib.rs‎

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ pub struct GenConfig {
5959
pub emit_mulhi: bool,
6060
pub emit_signed_mulhi: bool,
6161
pub emit_bitwise_binops: bool,
62+
pub emit_xor: bool,
6263
pub emit_prmt: bool,
6364
pub emit_not: bool,
6465
pub emit_clz: bool,
@@ -116,6 +117,7 @@ impl Default for GenConfig {
116117
emit_mulhi: true,
117118
emit_signed_mulhi: true,
118119
emit_bitwise_binops: true,
120+
emit_xor: true,
119121
emit_prmt: true,
120122
emit_not: true,
121123
emit_clz: true,
@@ -1027,6 +1029,7 @@ impl<'a> Generator<'a> {
10271029
self.cfg.emit_mulhi,
10281030
self.cfg.emit_signed_mulhi,
10291031
self.cfg.emit_bitwise_binops,
1032+
self.cfg.emit_xor,
10301033
)?;
10311034
Ok(Inst::Bin {
10321035
op,
@@ -1818,13 +1821,17 @@ fn pick_binop(
18181821
emit_mulhi: bool,
18191822
emit_signed_mulhi: bool,
18201823
emit_bitwise_binops: bool,
1824+
emit_xor: bool,
18211825
) -> Result<BinOp> {
18221826
let mut ops = vec![BinOp::Add, BinOp::Mul];
18231827
if emit_sub {
18241828
ops.push(BinOp::Sub);
18251829
}
18261830
if emit_bitwise_binops {
1827-
ops.extend([BinOp::And, BinOp::Or, BinOp::Xor]);
1831+
ops.extend([BinOp::And, BinOp::Or]);
1832+
if emit_xor {
1833+
ops.push(BinOp::Xor);
1834+
}
18281835
}
18291836
if emit_mulhi {
18301837
ops.push(BinOp::MulHi);
@@ -2385,6 +2392,29 @@ mod tests {
23852392
}
23862393
}
23872394

2395+
#[test]
2396+
fn xor_generation_can_be_disabled() {
2397+
let cfg = GenConfig {
2398+
emit_xor: false,
2399+
..GenConfig::default()
2400+
};
2401+
2402+
let mut saw_and = false;
2403+
let mut saw_or = false;
2404+
for seed in 0..512 {
2405+
let bytes = bytes_from_seed(seed, 4096);
2406+
let ptx = generate_from_bytes_with_config(&bytes, &cfg).unwrap();
2407+
assert!(
2408+
!has_mnemonic(&ptx, "xor.b32"),
2409+
"seed {seed:x} emitted xor.b32"
2410+
);
2411+
saw_and |= has_mnemonic(&ptx, "and.b32");
2412+
saw_or |= has_mnemonic(&ptx, "or.b32");
2413+
}
2414+
assert!(saw_and, "sample did not retain and.b32 coverage");
2415+
assert!(saw_or, "sample did not retain or.b32 coverage");
2416+
}
2417+
23882418
#[test]
23892419
fn prmt_generation_can_be_disabled() {
23902420
let cfg = GenConfig {

‎known-miscompiles/m036-mulhi-control-fold/NOTES.md‎

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -51,45 +51,40 @@ in_n = 32
5151
All lanes compute the same value and store to `out[0][1]`, so the repeated
5252
stores are benign. The standalone reproducer launches one thread.
5353

54+
On 2026-05-15, this was further reduced from 106 to 49 lines by deleting
55+
semantically dead arithmetic, folding constants on the taken path, and removing
56+
untaken-path scaffolding while preserving the same `0x80000092` (`-O0`) versus
57+
`0x80000090` (`-O2`/`-O3`) result. Several apparently dead control-flow edges
58+
still need to remain; deleting them causes optimized ptxas to stop taking the
59+
buggy fold.
60+
5461
## Correct Scalar Trace
5562

5663
The taken path is:
5764

5865
```text
59-
structured_if_0_then -> structured_if_1_else -> structured_if_2_else
60-
-> structured_if_3_then
66+
structured_if_1_else -> structured_if_2_done -> structured_if_3_done
6167
```
6268

6369
For `input[0] = 0x55ff25dc` and `in_n = 32`:
6470

6571
```text
66-
%r11 = 60
67-
%r18 = mad.lo.u32 60, 49375, 60 = 0x002d3480
68-
%p1 = true
69-
70-
%r19 = 8
71-
%r5 = 8 - 0x002d3480 = 0xffd2cb88
72+
%r5 = 0xffd2cb88
7273
%r6 = 32 * 0xffd2cb88 + 0xffd2cb88 = 0xfa2c3c88
73-
%r8 = 16 >> 11 = 0
7474
7575
%p6 = setp.ge.u32 19682, 0x55ff25dc = false
76-
%r3 = 0 ^ 0xffd2cb88 = 0xffd2cb88
77-
%r19 = clz.b32 0 = 32
78-
%r10 = 0x002d3480 & 2 = 0
79-
%r15 = ((0xffd2cb88 * 0xfa2c3c88 + 31152) >> 26) = 6
76+
%r1 = mad.lo.u32 0xffd2cb88, 0xfa2c3c88, 31152 = 0x1b1079f0
77+
%r15 = 0x1b1079f0 >> 26 = 6
8078
%p14 = false
8179
%r0 = 0x40000000
82-
%r8 = brev.b32 0x20000000 = 4
80+
%r8 = 4
8381
%r16 = 0x40000000 ^ 33145 = 0x40008179
84-
%r2 = popc.b32 24696 = 6
8582
%r4 = mul.hi.s32 6, 0x40008179 = 1
86-
%r12 = 0xffd2cb88 * 0 + 1 = 1
8783
%p18 = setp.eq.u32 1, 0 = false
8884
89-
%r13 = mul.hi.s32 489, 0xffd2cb88 = 0xffffffff
9085
%r14 = mad.lo.u32 4, 0x20000000, 0xffffffff = 0x7fffffff
9186
%r19 = 1 - 0x7fffffff = 0x80000002
92-
%r1 = mad.lo.u32 12, 12, 0x80000002 = 0x80000092
87+
%r1 = 0x80000002 + 144 = 0x80000092
9388
%p21 = setp.le.u32 4, 0x80000002 = true
9489
store %r1 to out[0][1]
9590
```

‎known-miscompiles/m036-mulhi-control-fold/reduced.ptx‎

Lines changed: 16 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -8,99 +8,42 @@
88
.param .u32 in_n
99
)
1010
{
11-
.reg .pred %p<26>;
12-
.reg .b32 %r<22>;
13-
.reg .b64 %rd<8>;
11+
.reg .pred %p<22>;
12+
.reg .b32 %r<20>;
13+
.reg .b64 %rd<5>;
1414

1515
ld.param.u64 %rd0, [in_ptr];
1616
ld.param.u64 %rd1, [out_ptr];
1717
ld.param.u32 %r0, [in_n];
1818
cvta.to.global.u64 %rd2, %rd0;
1919
ld.global.u32 %r2, [%rd2];
20-
mov.u32 %r17, 0;
21-
mov.u32 %r3, 0;
22-
mov.u32 %r7, 0;
23-
setp.eq.u32 %p24, 0, 1;
2420

25-
mul.hi.s32 %r14, %r0, 16;
26-
mov.u32 %r11, 60;
27-
mad.lo.u32 %r18, %r11, 49375, %r11;
28-
shr.u32 %r13, 69160512, 25;
29-
mad.lo.u32 %r5, 4, %r14, %r0;
30-
setp.eq.u32 %p1, 0, 0;
31-
@%p1 bra structured_if_0_then;
32-
bra structured_if_0_else;
33-
structured_if_0_then:
34-
setp.eq.u32 %p3, 0, 0;
35-
selp.b32 %r19, 8, %r17, %p3;
36-
sub.u32 %r5, %r19, %r18;
21+
mov.u32 %r5, 0xffd2cb88;
3722
mad.lo.u32 %r6, %r0, %r5, %r5;
38-
shr.u32 %r8, 16, 11;
39-
bra structured_if_0_done;
40-
structured_if_0_else:
41-
bra structured_if_0_done;
42-
structured_if_0_done:
4323
setp.ge.u32 %p6, 19682, %r2;
44-
@%p6 bra structured_if_1_then;
45-
bra structured_if_1_else;
46-
structured_if_1_then:
47-
mad.lo.u32 %r19, %r19, 4194304, 27678;
48-
clz.b32 %r8, %r17;
49-
setp.ge.u32 %p9, %r19, 53078;
50-
selp.b32 %r17, 4294967295, %r7, %p9;
51-
dp4a.s32.u32 %r10, %r3, %r7, 62161;
24+
@!%p6 bra structured_if_1_else;
25+
clz.b32 %r8, 0;
5226
bra structured_if_1_done;
5327
structured_if_1_else:
54-
xor.b32 %r3, %r8, %r5;
55-
mad.lo.u32 %r2, %r18, %r19, 49358;
56-
mul.hi.s32 %r1, %r13, 33098;
57-
clz.b32 %r19, %r8;
58-
cvt.u32.u16 %r2, %r2;
59-
and.b32 %r10, %r18, %r13;
60-
mad.lo.u32 %r1, %r3, %r6, 31152;
61-
mad.lo.u32 %r8, %r19, 19382, %r5;
62-
shr.u32 %r15, %r19, 11;
28+
mad.lo.u32 %r1, %r5, %r6, 31152;
6329
shr.u32 %r15, %r1, 26;
64-
mad.lo.u32 %r13, %r6, %r5, 1469367838;
65-
sub.u32 %r6, 12, %r10;
6630
setp.ge.u32 %p14, %r15, 8;
6731
selp.b32 %r0, %r1, 1073741824, %p14;
68-
shr.u32 %r7, 27123, 21;
69-
brev.b32 %r8, 536870912;
32+
mov.u32 %r8, 4;
7033
xor.b32 %r16, %r0, 33145;
71-
bfe.u32 %r17, %r11, 25, 1;
72-
popc.b32 %r2, 24696;
73-
and.b32 %r1, %r0, %r19;
74-
mul.hi.s32 %r4, %r2, %r16;
75-
mad.lo.u32 %r12, %r5, %r10, %r4;
76-
setp.eq.u32 %p18, %r12, %r7;
77-
@%p18 bra structured_if_2_then;
78-
bra structured_if_2_else;
79-
structured_if_2_then:
80-
bra structured_if_2_done;
81-
structured_if_2_else:
82-
mul.hi.s32 %r13, 489, %r3;
83-
mad.lo.u32 %r14, %r8, 536870912, %r13;
34+
mul.hi.s32 %r4, 6, %r16;
35+
setp.eq.u32 %p18, %r4, 0;
36+
@%p18 bra structured_if_2_done;
37+
mad.lo.u32 %r14, %r8, 536870912, 0xffffffff;
8438
sub.u32 %r19, %r4, %r14;
85-
mad.lo.u32 %r1, %r6, %r6, %r19;
86-
and.b32 %r4, 24045, %r0;
87-
bra structured_if_2_done;
39+
add.u32 %r1, %r19, 144;
8840
structured_if_2_done:
89-
bra structured_if_1_done;
9041
structured_if_1_done:
9142
setp.le.u32 %p21, %r8, %r19;
92-
@%p21 bra structured_if_3_then;
93-
bra structured_if_3_else;
94-
structured_if_3_then:
95-
bra structured_if_3_done;
96-
structured_if_3_else:
97-
selp.b32 %r1, 5032, %r17, %p24;
98-
bra structured_if_3_done;
43+
@%p21 bra structured_if_3_done;
44+
mov.u32 %r1, 0;
9945
structured_if_3_done:
100-
bra exit;
10146

102-
exit:
103-
cvta.to.global.u64 %rd4, %rd1;
104-
st.global.u32 [%rd4 + 4], %r1;
47+
st.global.u32 [%rd1 + 4], %r1;
10548
ret;
10649
}

0 commit comments

Comments
 (0)