Skip to content

Suspected LLVM miscompilation of bool to int cast #112170

Closed
@cbeuw

Description

@cbeuw

The reproduction is fuzzer-generated custom MIR then (somewhat) minimised. I couldn't rewrite it into surface Rust as there are back edges to non-dominating basic blocks (they can't be turned into surface Rust loops). It's possible that rustc considers such control flow invalid, though this is likely an LLVM miscompilation and such control flow is valid at LLVM IR level. Miri also does not complain.

The actual execution flow has no loops, all statements are executed only once. Indented BBs are not taken, but still required for the reproduction.

#![feature(custom_mir, core_intrinsics)]
extern crate core;
use core::intrinsics::mir::*;

pub fn print_var(x: usize) {
    println!("{x}");
}


#[custom_mir(dialect = "runtime", phase = "initial")]
pub fn fn1(){
mir! {
let _2: i128;
let _3: i16;
let _4: usize;
let _6: (u32, f64);
let _7: (u32, f64);
let _8: f64;
let _9: isize;
let _10: *mut (u8,);
let _12: *mut i8;
let _15: (u32, *mut (u8,));
let _16: isize;
let _17: usize;
let _18: (u128, u8, bool, u32, char, i8, u32, i32);
let _18_ptr: *mut (u128, u8, bool, u32, char, i8, u32, i32);
let _24: (i32, ([f32; 6],));
let _26: f32;
let _27: f32;
let _30: *mut i8;
let _31: isize;
let _32: isize;
let _33: u8;
let _34: u64;
let _40: f32;
let _41: bool;
let _45: f64;
let _51: *mut (u32, f64);
let _52: *mut i8;
let _55: u32;
let _60: u64;
let _62: (u8,);
let _64: usize;
let _68: *mut (u32, f64);
let _85: *mut (u32, f64);
let _88: (u8,);
let _98: (u8,);
let _106: f32;
let _109: (u8,);
let _111: *mut i8;
let _113: i16;
let _115: u64;
let _335: ();
{
_2 = 0;
_4 = 0;
_6 = (0, 0.);
_7 = _6;
_15.0 = _6.0;
_18_ptr = core::ptr::addr_of_mut!(_18);
_12 = core::ptr::addr_of_mut!((*_18_ptr).5);
(*_18_ptr).7 = 36514_u16 as i32;
_17 = !_4;
Goto(bb2)
}
bb2 = {
_16 = _17 as isize;
_26 = _7.1 as f32;
_27 = _26;
_8 = _6.1 + _7.1;
Goto(bb3)
}
bb3 = {
_24.0 = (*_18_ptr).7;
(*_18_ptr).1 = 170_u8;
*_12 = 49;
(*_18_ptr).7 = -221108602_i32;
Goto(bb6)
}
    bb4 = {
    _27 = _26;
    Goto(bb3)
    }
    bb5 = {
    _12 = core::ptr::addr_of_mut!((*_18_ptr).5);
    Goto(bb2)
    }
bb6 = {
_3 = 18339_i16;
_31 = _6.1 as isize;
Goto(bb7)
}
bb7 = {
match (*_18_ptr).5 {
0 => bb8,
49 => bb10,
_ => ret
}
}
    bb8 = {
    _31 = _9 << _15.0;
    Goto(bb7)
    }
bb10 = {
match (*_18_ptr).1 {
1 => bb2,
2 => bb3,
3 => bb4,
4 => bb5,
6 => bb7,
170 => bb12,
_ => ret
}
}
bb12 = {
_32 = _31;
match (*_18_ptr).7 {
-221108602 => bb13,
_ => bb9
}
}
    bb9 = {
    _17 = _4;
    Goto(bb3)
    }
bb13 = {
_30 = core::ptr::addr_of_mut!((*_18_ptr).5);
Goto(bb20)
}
    bb16 = {
    match (*_18_ptr).1 {
    2 => bb3,
    3 => bb4,
    6 => bb7,
    170 => bb12,
    _ => ret
    }
    }
    bb19 = {
    match (*_18_ptr).5 {
    0 => bb8,
    49 => bb10,
    _ => bb9
    }
    }
bb20 = {
match _3 {
0 => bb6,
18339 => bb23,
_ => bb19
}
}
bb23 = {
_51 = core::ptr::addr_of_mut!(_6);
_52 = _12;
(*_18_ptr).0 = 66683941810808954240874936259049749206_u128;
(*_18_ptr).3 = 3241810161_u32;
match (*_18_ptr).3 {
3241810161 => bb25,
_ => bb5
}
}
bb25 = {
_34 = 2204794180844018677_u64;
_33 = (*_18_ptr).0 as u8;
(*_51).1 = _7.1 / f64::NAN;
Goto(bb27)
}
bb27 = {
_7.1 = _6.1;
(*_18_ptr).7 = _24.0;
(*_51) = _7;
_40 = _26 - _26;
Goto(bb28)
}
bb28 = {
_12 = _52;
_55 = _40 as u32;
(*_18_ptr).1 = _33;
Goto(bb29)
}
bb29 = {
match (*_18_ptr).3 {
3241810161 => bb31,
_ => bb30
}
}
    bb30 = {
    match (*_18_ptr).7 {
    3 => bb16,
    -221108602 => bb20,
    _ => bb19
    }
    }
bb31 = {
_10 = core::ptr::addr_of_mut!(_62);
(*_12) = 115_i8;
_68 = core::ptr::addr_of_mut!(_7);
_51 = _68;
match _34 {
2204794180844018677 => bb33,
_ => bb23
}
}
bb33 = {
(*_68).0 = _55;
(*_10).0 = _33;
_45 = (*_68).1;
(*_68).1 = _8;
_106 = _27;
_85 = core::ptr::addr_of_mut!(_6);
_60 = _34;
_111 = _12;
_41 = _45 != (*_68).1;
Goto(bb52)
}
bb52 = {
_98 = *_10;
_113 = _106 as i16;
_33 = !_98.0;
_26 = _106 / f32::NAN;
_52 = _30;
_3 = _113 >> (*_111);
Goto(bb67)
}
bb67 = {
*_51 = *_85;
_109.0 = (*_10).0 | _33;
_115 = _60 << (*_18_ptr).3;
_88 = _109;
(*_18_ptr) = (169547000073183258424114496411693076442_u128, 225_u8, true, 3440650345_u32, '\u{f03a2}', (-32_i8), 1579607197_u32, (-460487397_i32));
(*_10).0 = _2 as u8;
match *_111 {
1 => bb27,
3 => bb67,
-32 => bb75,
_ => bb19
}
}
bb75 = {
(*_10) = _88;
_34 = !_115;
_51 = _85;
(*_18_ptr).5 = -20;
Goto(bb138)
}
bb138 = {
match *_12 {
0 => bb28,
2 => bb52,
3 => bb155,
-20 => bb158,
_ => ret
}
}
    bb155 = {
    _12 = _52;
    Goto(bb138)
    }
bb158 = {
_64 = _41 as usize; // _41 is a boolean so this has to print 0 or 1
Call(_335, ret, print_var(Move(_64)))
}
ret = {
Return()
}
}
}

pub fn main() {
    fn1();
}

In Miri, the program is UB-free under both Stacked Borrows and Tree Borrows. The correct result is 1

$ rustc -Zmir-opt-level=0 -Copt-level=1 255118.rs && ./255118
1

at -Copt-level>=2, it prints 255, which is clearly wrong as the value came from a bool to int cast.

$ rustc -Zmir-opt-level=0 -Copt-level=2 255118.rs && ./255118
255

-Zmir-opt-level>=1 prevents the miscompilation, -Cno-prepopulate-passes also does. Implying either the generated IR has UB or this is a bug in LLVM

$ rustc -Zmir-opt-level=1 -Copt-level=3 255118.rs && ./255118
1
$ rustc -Zmir-opt-level=0 -Copt-level=3 -Cno-prepopulate-passes 255118.rs && ./255118
1

Only reproducible on x86_64 Linux, not Apple Silicon macOS

(This is still reproducible even with llvm/llvm-project@97f0e7b applied, so it's a separate bug)

I tried to get a standalone LLVM IR file using @Nilstrieb's approach in https://github.com/Nilstrieb/rlo-issue-112061 by having print_var in helper C code, but I couldn't reproduce the misoptimisation with llc since the pre-populated passes from rustc are required for the reproduction, which llc -On does not replicate (or not in the same order)

cc @nikic

Metadata

Metadata

Assignees

Labels

A-LLVMArea: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues.I-unsoundIssue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/SoundnessP-highHigh priorityT-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions