Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Suspected LLVM miscompilation of bool to int cast #112170

Closed
cbeuw opened this issue Jun 1, 2023 · 5 comments · Fixed by #112312
Closed

Suspected LLVM miscompilation of bool to int cast #112170

cbeuw opened this issue Jun 1, 2023 · 5 comments · Fixed by #112312
Assignees
Labels
A-LLVM Area: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues. I-unsound Issue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/Soundness P-high High priority T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@cbeuw
Copy link
Contributor

cbeuw commented Jun 1, 2023

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

@nikic
Copy link
Contributor

nikic commented Jun 1, 2023

Cleaned up input IR:

target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"

declare void @print_var(i64 noundef %0)

define void @test() nounwind {
start:
  %_37 = alloca i8, align 1
  %_30 = alloca i8, align 1
  %_27 = alloca ptr, align 8
  %_26 = alloca ptr, align 8
  %_22 = alloca i64, align 8
  %_21 = alloca i8, align 1
  %_19 = alloca i64, align 8
  %_17 = alloca float, align 4
  %_16 = alloca float, align 4
  %_15 = alloca { i32, { [6 x float] } }, align 4
  %_13 = alloca { i128, i32, i32, i32, i8, i8, i8, [1 x i8], i32, [1 x i32] }, align 8
  %_12 = alloca i64, align 8
  %_10 = alloca { i32, ptr }, align 8
  %_9 = alloca ptr, align 8
  %_7 = alloca i64, align 8
  %_5 = alloca { i32, double }, align 8
  %_4 = alloca { i32, double }, align 8
  %_2 = alloca i16, align 2
  store i32 0, ptr %_4, align 8
  %0 = getelementptr inbounds { i32, double }, ptr %_4, i32 0, i32 1
  store double 0.000000e+00, ptr %0, align 8
  %1 = getelementptr inbounds { i32, double }, ptr %_4, i32 0, i32 0
  %2 = load i32, ptr %1, align 8
  %3 = getelementptr inbounds { i32, double }, ptr %_4, i32 0, i32 1
  %4 = load double, ptr %3, align 8
  %5 = getelementptr inbounds { i32, double }, ptr %_5, i32 0, i32 0
  store i32 %2, ptr %5, align 8
  %6 = getelementptr inbounds { i32, double }, ptr %_5, i32 0, i32 1
  store double %4, ptr %6, align 8
  %7 = load i32, ptr %_4, align 8
  store i32 %7, ptr %_10, align 8
  %8 = getelementptr inbounds { i128, i32, i32, i32, i8, i8, i8, [1 x i8], i32, [1 x i32] }, ptr %_13, i32 0, i32 5
  store ptr %8, ptr %_9, align 8
  %9 = getelementptr inbounds { i128, i32, i32, i32, i8, i8, i8, [1 x i8], i32, [1 x i32] }, ptr %_13, i32 0, i32 8
  store i32 36514, ptr %9, align 8
  store i64 -1, ptr %_12, align 8
  br label %bb1

bb1:                                              ; preds = %bb4, %bb8, %start
  %_11 = load i64, ptr %_12, align 8
  %10 = getelementptr inbounds { i32, double }, ptr %_5, i32 0, i32 1
  %11 = load double, ptr %10, align 8
  %12 = fptrunc double %11 to float
  store float %12, ptr %_16, align 4
  %13 = load float, ptr %_16, align 4
  store float %13, ptr %_17, align 4
  %14 = getelementptr inbounds { i32, double }, ptr %_4, i32 0, i32 1
  %15 = load double, ptr %14, align 8
  %16 = getelementptr inbounds { i32, double }, ptr %_5, i32 0, i32 1
  %17 = load double, ptr %16, align 8
  %_6 = fadd double %15, %17
  br label %bb2

bb2:                                              ; preds = %bb3, %bb10, %bb12, %bb8, %bb1
  %18 = getelementptr inbounds { i128, i32, i32, i32, i8, i8, i8, [1 x i8], i32, [1 x i32] }, ptr %_13, i32 0, i32 8
  %19 = load i32, ptr %18, align 8
  store i32 %19, ptr %_15, align 4
  %20 = getelementptr inbounds { i128, i32, i32, i32, i8, i8, i8, [1 x i8], i32, [1 x i32] }, ptr %_13, i32 0, i32 4
  store i8 -86, ptr %20, align 4
  %21 = load ptr, ptr %_9, align 8
  store i8 49, ptr %21, align 1
  %22 = getelementptr inbounds { i128, i32, i32, i32, i8, i8, i8, [1 x i8], i32, [1 x i32] }, ptr %_13, i32 0, i32 8
  store i32 -221108602, ptr %22, align 8
  br label %bb5

bb5:                                              ; preds = %bb14, %bb2
  store i16 18339, ptr %_2, align 2
  %23 = getelementptr inbounds { i32, double }, ptr %_4, i32 0, i32 1
  %24 = load double, ptr %23, align 8
  %25 = call i64 @llvm.fptosi.sat.i64.f64(double %24)
  store i64 %25, ptr %_19, align 8
  br label %bb6

bb6:                                              ; preds = %bb7, %bb12, %bb8, %bb5
  %26 = getelementptr inbounds { i128, i32, i32, i32, i8, i8, i8, [1 x i8], i32, [1 x i32] }, ptr %_13, i32 0, i32 5
  %27 = load i8, ptr %26, align 1
  switch i8 %27, label %bb28 [
    i8 0, label %bb7
    i8 49, label %bb8
  ]

bb28:                                             ; preds = %bb27, %bb25, %bb12, %bb8, %bb6
  ret void

bb7:                                              ; preds = %bb13, %bb6
  %28 = load i64, ptr %_7, align 8
  %29 = load i32, ptr %_10, align 8
  %30 = zext i32 %29 to i64
  %31 = and i64 %30, 63
  %32 = shl i64 %28, %31
  store i64 %32, ptr %_19, align 8
  br label %bb6

bb8:                                              ; preds = %bb13, %bb6
  %33 = getelementptr inbounds { i128, i32, i32, i32, i8, i8, i8, [1 x i8], i32, [1 x i32] }, ptr %_13, i32 0, i32 4
  %34 = load i8, ptr %33, align 4
  switch i8 %34, label %bb28 [
    i8 1, label %bb1
    i8 2, label %bb2
    i8 3, label %bb3
    i8 4, label %bb4
    i8 6, label %bb6
    i8 -86, label %bb9
  ]

bb3:                                              ; preds = %bb12, %bb8
  %35 = load float, ptr %_16, align 4
  store float %35, ptr %_17, align 4
  br label %bb2

bb4:                                              ; preds = %bb15, %bb8
  %36 = getelementptr inbounds { i128, i32, i32, i32, i8, i8, i8, [1 x i8], i32, [1 x i32] }, ptr %_13, i32 0, i32 5
  store ptr %36, ptr %_9, align 8
  br label %bb1

bb9:                                              ; preds = %bb12, %bb8
  %_20 = load i64, ptr %_19, align 8
  %37 = getelementptr inbounds { i128, i32, i32, i32, i8, i8, i8, [1 x i8], i32, [1 x i32] }, ptr %_13, i32 0, i32 8
  %38 = load i32, ptr %37, align 8
  %39 = icmp eq i32 %38, -221108602
  br i1 %39, label %bb11, label %bb10

bb11:                                             ; preds = %bb9
  %_18 = getelementptr inbounds { i128, i32, i32, i32, i8, i8, i8, [1 x i8], i32, [1 x i32] }, ptr %_13, i32 0, i32 5
  br label %bb14

bb10:                                             ; preds = %bb13, %bb9
  store i64 0, ptr %_12, align 8
  br label %bb2

bb14:                                             ; preds = %bb19, %bb11
  %40 = load i16, ptr %_2, align 2
  switch i16 %40, label %bb13 [
    i16 0, label %bb5
    i16 18339, label %bb15
  ]

bb13:                                             ; preds = %bb23, %bb19, %bb14
  %41 = getelementptr inbounds { i128, i32, i32, i32, i8, i8, i8, [1 x i8], i32, [1 x i32] }, ptr %_13, i32 0, i32 5
  %42 = load i8, ptr %41, align 1
  switch i8 %42, label %bb10 [
    i8 0, label %bb7
    i8 49, label %bb8
  ]

bb15:                                             ; preds = %bb20, %bb14
  store ptr %_4, ptr %_26, align 8
  %43 = load ptr, ptr %_9, align 8
  store ptr %43, ptr %_27, align 8
  store i128 66683941810808954240874936259049749206, ptr %_13, align 8
  %44 = getelementptr inbounds { i128, i32, i32, i32, i8, i8, i8, [1 x i8], i32, [1 x i32] }, ptr %_13, i32 0, i32 1
  store i32 -1053157135, ptr %44, align 8
  %45 = getelementptr inbounds { i128, i32, i32, i32, i8, i8, i8, [1 x i8], i32, [1 x i32] }, ptr %_13, i32 0, i32 1
  %46 = load i32, ptr %45, align 8
  %47 = icmp eq i32 %46, -1053157135
  br i1 %47, label %bb16, label %bb4

bb16:                                             ; preds = %bb15
  store i64 2204794180844018677, ptr %_22, align 8
  %48 = load i128, ptr %_13, align 8
  %49 = trunc i128 %48 to i8
  store i8 %49, ptr %_21, align 1
  %50 = load ptr, ptr %_26, align 8
  %51 = getelementptr inbounds { i32, double }, ptr %50, i32 0, i32 1
  %52 = getelementptr inbounds { i32, double }, ptr %_5, i32 0, i32 1
  %53 = load double, ptr %52, align 8
  %54 = fdiv double %53, 0x7FF8000000000000
  store double %54, ptr %51, align 8
  br label %bb17

bb17:                                             ; preds = %bb23, %bb16
  %55 = getelementptr inbounds { i32, double }, ptr %_5, i32 0, i32 1
  %56 = getelementptr inbounds { i32, double }, ptr %_4, i32 0, i32 1
  %57 = load double, ptr %56, align 8
  store double %57, ptr %55, align 8
  %58 = getelementptr inbounds { i128, i32, i32, i32, i8, i8, i8, [1 x i8], i32, [1 x i32] }, ptr %_13, i32 0, i32 8
  %59 = load i32, ptr %_15, align 4
  store i32 %59, ptr %58, align 8
  %60 = load ptr, ptr %_26, align 8
  %61 = getelementptr inbounds { i32, double }, ptr %_5, i32 0, i32 0
  %62 = load i32, ptr %61, align 8
  %63 = getelementptr inbounds { i32, double }, ptr %_5, i32 0, i32 1
  %64 = load double, ptr %63, align 8
  %65 = getelementptr inbounds { i32, double }, ptr %60, i32 0, i32 0
  store i32 %62, ptr %65, align 8
  %66 = getelementptr inbounds { i32, double }, ptr %60, i32 0, i32 1
  store double %64, ptr %66, align 8
  %67 = load float, ptr %_16, align 4
  %68 = load float, ptr %_16, align 4
  %_23 = fsub float %67, %68
  br label %bb18

bb18:                                             ; preds = %bb25, %bb17
  %69 = load ptr, ptr %_27, align 8
  store ptr %69, ptr %_9, align 8
  %_28 = call i32 @llvm.fptoui.sat.i32.f32(float %_23)
  %70 = getelementptr inbounds { i128, i32, i32, i32, i8, i8, i8, [1 x i8], i32, [1 x i32] }, ptr %_13, i32 0, i32 4
  %71 = load i8, ptr %_21, align 1
  store i8 %71, ptr %70, align 4
  %72 = getelementptr inbounds { i128, i32, i32, i32, i8, i8, i8, [1 x i8], i32, [1 x i32] }, ptr %_13, i32 0, i32 1
  %73 = load i32, ptr %72, align 8
  %74 = icmp eq i32 %73, -1053157135
  br i1 %74, label %bb20, label %bb19

bb20:                                             ; preds = %bb18
  %75 = load ptr, ptr %_9, align 8
  store i8 115, ptr %75, align 1
  store ptr %_5, ptr %_26, align 8
  %76 = load i64, ptr %_22, align 8
  %77 = icmp eq i64 %76, 2204794180844018677
  br i1 %77, label %bb21, label %bb15

bb19:                                             ; preds = %bb18
  %78 = getelementptr inbounds { i128, i32, i32, i32, i8, i8, i8, [1 x i8], i32, [1 x i32] }, ptr %_13, i32 0, i32 8
  %79 = load i32, ptr %78, align 8
  switch i32 %79, label %bb13 [
    i32 3, label %bb12
    i32 -221108602, label %bb14
  ]

bb12:                                             ; preds = %bb19
  %80 = getelementptr inbounds { i128, i32, i32, i32, i8, i8, i8, [1 x i8], i32, [1 x i32] }, ptr %_13, i32 0, i32 4
  %81 = load i8, ptr %80, align 4
  switch i8 %81, label %bb28 [
    i8 2, label %bb2
    i8 3, label %bb3
    i8 6, label %bb6
    i8 -86, label %bb9
  ]

bb21:                                             ; preds = %bb20
  store i32 %_28, ptr %_5, align 8
  %82 = load i8, ptr %_21, align 1
  store i8 %82, ptr %_30, align 1
  %83 = getelementptr inbounds { i32, double }, ptr %_5, i32 0, i32 1
  %_25 = load double, ptr %83, align 8
  %84 = getelementptr inbounds { i32, double }, ptr %_5, i32 0, i32 1
  store double %_6, ptr %84, align 8
  %_36 = load float, ptr %_17, align 4
  %_29 = load i64, ptr %_22, align 8
  %_38 = load ptr, ptr %_9, align 8
  %85 = getelementptr inbounds { i32, double }, ptr %_5, i32 0, i32 1
  %86 = load double, ptr %85, align 8
  %_24 = fcmp une double %_25, %86
  br label %bb22

bb22:                                             ; preds = %bb25, %bb21
  %_35 = load i8, ptr %_30, align 1
  %_39 = call i16 @llvm.fptosi.sat.i16.f32(float %_36)
  %87 = xor i8 %_35, -1
  store i8 %87, ptr %_21, align 1
  %88 = fdiv float %_36, 0x7FF8000000000000
  store float %88, ptr %_16, align 4
  store ptr %_18, ptr %_27, align 8
  %89 = load i8, ptr %_38, align 1
  %90 = zext i8 %89 to i16
  %91 = and i16 %90, 15
  %92 = ashr i16 %_39, %91
  store i16 %92, ptr %_2, align 2
  br label %bb23

bb23:                                             ; preds = %bb23, %bb22
  %93 = load ptr, ptr %_26, align 8
  %94 = getelementptr inbounds { i32, double }, ptr %_4, i32 0, i32 0
  %95 = load i32, ptr %94, align 8
  %96 = getelementptr inbounds { i32, double }, ptr %_4, i32 0, i32 1
  %97 = load double, ptr %96, align 8
  %98 = getelementptr inbounds { i32, double }, ptr %93, i32 0, i32 0
  store i32 %95, ptr %98, align 8
  %99 = getelementptr inbounds { i32, double }, ptr %93, i32 0, i32 1
  store double %97, ptr %99, align 8
  %100 = load i8, ptr %_30, align 1
  %101 = load i8, ptr %_21, align 1
  %102 = or i8 %100, %101
  store i8 %102, ptr %_37, align 1
  %103 = getelementptr inbounds { i128, i32, i32, i32, i8, i8, i8, [1 x i8], i32, [1 x i32] }, ptr %_13, i32 0, i32 1
  %104 = load i32, ptr %103, align 8
  %105 = zext i32 %104 to i64
  %106 = and i64 %105, 63
  %_40 = shl i64 %_29, %106
  %_34 = load i8, ptr %_37, align 1
  store i128 169547000073183258424114496411693076442, ptr %_13, align 8
  %107 = getelementptr inbounds { i128, i32, i32, i32, i8, i8, i8, [1 x i8], i32, [1 x i32] }, ptr %_13, i32 0, i32 4
  store i8 -31, ptr %107, align 4
  %108 = getelementptr inbounds { i128, i32, i32, i32, i8, i8, i8, [1 x i8], i32, [1 x i32] }, ptr %_13, i32 0, i32 6
  store i8 1, ptr %108, align 2
  %109 = getelementptr inbounds { i128, i32, i32, i32, i8, i8, i8, [1 x i8], i32, [1 x i32] }, ptr %_13, i32 0, i32 1
  store i32 -854316951, ptr %109, align 8
  %110 = getelementptr inbounds { i128, i32, i32, i32, i8, i8, i8, [1 x i8], i32, [1 x i32] }, ptr %_13, i32 0, i32 3
  store i32 983970, ptr %110, align 8
  %111 = getelementptr inbounds { i128, i32, i32, i32, i8, i8, i8, [1 x i8], i32, [1 x i32] }, ptr %_13, i32 0, i32 5
  store i8 -32, ptr %111, align 1
  %112 = getelementptr inbounds { i128, i32, i32, i32, i8, i8, i8, [1 x i8], i32, [1 x i32] }, ptr %_13, i32 0, i32 2
  store i32 1579607197, ptr %112, align 4
  %113 = getelementptr inbounds { i128, i32, i32, i32, i8, i8, i8, [1 x i8], i32, [1 x i32] }, ptr %_13, i32 0, i32 8
  store i32 -460487397, ptr %113, align 8
  store i8 0, ptr %_30, align 1
  %114 = load i8, ptr %_38, align 1
  switch i8 %114, label %bb13 [
    i8 1, label %bb17
    i8 3, label %bb23
    i8 -32, label %bb24
  ]

bb24:                                             ; preds = %bb23
  store i8 %_34, ptr %_30, align 1
  %115 = xor i64 %_40, -1
  store i64 %115, ptr %_22, align 8
  store ptr %_4, ptr %_26, align 8
  %116 = getelementptr inbounds { i128, i32, i32, i32, i8, i8, i8, [1 x i8], i32, [1 x i32] }, ptr %_13, i32 0, i32 5
  store i8 -20, ptr %116, align 1
  br label %bb25

bb25:                                             ; preds = %bb26, %bb24
  %117 = load ptr, ptr %_9, align 8
  %118 = load i8, ptr %117, align 1
  switch i8 %118, label %bb28 [
    i8 0, label %bb18
    i8 2, label %bb22
    i8 3, label %bb26
    i8 -20, label %bb27
  ]

bb26:                                             ; preds = %bb25
  %119 = load ptr, ptr %_27, align 8
  store ptr %119, ptr %_9, align 8
  br label %bb25

bb27:                                             ; preds = %bb25
  %_31 = zext i1 %_24 to i64
  call void @print_var(i64 noundef %_31)
  br label %bb28
}

declare i64 @llvm.fptosi.sat.i64.f64(double) #4

declare i32 @llvm.fptoui.sat.i32.f32(float) #4

declare i16 @llvm.fptosi.sat.i16.f32(float) #4

After opt -O2:

; ModuleID = 'test622.ll'
source_filename = "test622.ll"
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"

declare void @print_var(i64 noundef) local_unnamed_addr

; Function Attrs: nounwind
define void @test() local_unnamed_addr #0 {
start:
  %_5.sroa.0 = alloca i32, align 8
  %_5.sroa.4 = alloca double, align 8
  %_4.sroa.0 = alloca i32, align 8
  %_4.sroa.3 = alloca double, align 8
  store double 0.000000e+00, ptr %_4.sroa.3, align 8
  store i32 0, ptr %_5.sroa.0, align 8
  store double 0.000000e+00, ptr %_5.sroa.4, align 8
  %0 = fptrunc double 0.000000e+00 to float
  %_6 = fadd double 0.000000e+00, 0.000000e+00
  br label %bb2.outer1463

bb2.outer1463:                                    ; preds = %bb2.outer1463, %start
  %_13.sroa.25.1.ph1464 = phi i32 [ 36514, %start ], [ -221108602, %bb2.outer1463 ]
  %_16.0.ph1465 = phi float [ %0, %start ], [ %_16.0.ph1465, %bb2.outer1463 ]
  %1 = icmp eq i32 -221108602, -221108602
  br i1 %1, label %bb15.preheader, label %bb2.outer1463

bb15.preheader:                                   ; preds = %bb2.outer1463
  store double 0x7FF8000000000000, ptr %_5.sroa.4, align 8
  %_5.sroa.0.0._5.sroa.0.0. = load i32, ptr %_5.sroa.0, align 8
  store i32 %_5.sroa.0.0._5.sroa.0.0., ptr %_4.sroa.0, align 8
  store double 0x7FF8000000000000, ptr %_4.sroa.3, align 8
  %_23332 = fsub float %_16.0.ph1465, %_16.0.ph1465
  %_28333 = tail call i32 @llvm.fptoui.sat.i32.f32(float %_23332)
  store i32 %_28333, ptr %_5.sroa.0, align 8
  %_5.sroa.4.0._5.sroa.4.8._25 = load double, ptr %_5.sroa.4, align 8
  store double %_6, ptr %_5.sroa.4, align 8
  %_4.sroa.0.0._4.sroa.0.0. = load i32, ptr %_4.sroa.0, align 8
  %_4.sroa.3.0._4.sroa.3.8. = load double, ptr %_4.sroa.3, align 8
  store i32 %_4.sroa.0.0._4.sroa.0.0., ptr %_5.sroa.0, align 8
  store double %_4.sroa.3.0._4.sroa.3.8., ptr %_5.sroa.4, align 8
  %_24.le = fcmp une double %_5.sroa.4.0._5.sroa.4.8._25, %_6
  %_31 = zext i1 %_24.le to i64
  tail call void @print_var(i64 noundef %_31) #0
  ret void
}

; Function Attrs: mustprogress nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare i32 @llvm.fptoui.sat.i32.f32(float) #1

attributes #0 = { nounwind }
attributes #1 = { mustprogress nocallback nofree nosync nounwind speculatable willreturn memory(none) }

After llc:

	.text
	.file	"test622.ll"
	.globl	test                            # -- Begin function test
	.p2align	4, 0x90
	.type	test,@function
test:                                   # @test
# %bb.0:                                # %start
	movq	$0, -24(%rsp)
	movl	$0, -32(%rsp)
	movq	$0, -8(%rsp)
	xorl	%eax, %eax
	.p2align	4, 0x90
.LBB0_1:                                # %bb2.outer1463
                                        # =>This Inner Loop Header: Depth=1
	testb	%al, %al
	jne	.LBB0_1
# %bb.2:                                # %bb15.preheader
	movl	-32(%rsp), %eax
	movl	%eax, -16(%rsp)
	movabsq	$9221120237041090560, %rax      # imm = 0x7FF8000000000000
	movq	%rax, -24(%rsp)
	movq	%rax, -8(%rsp)
	movl	$255, %edi
	jmp	print_var@PLT                   # TAILCALL
.Lfunc_end0:
	.size	test, .Lfunc_end0-test
                                        # -- End function
	.section	".note.GNU-stack","",@progbits

So this looks like a backend bug.

@cbeuw
Copy link
Contributor Author

cbeuw commented Jun 1, 2023

Yes it's an x86 backend bug llvm/llvm-project#63055

@nikic nikic added A-LLVM Area: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues. I-unsound Issue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/Soundness labels Jun 1, 2023
@rustbot rustbot added the I-prioritize Issue: Indicates that prioritization has been requested for this issue. label Jun 1, 2023
@apiraino
Copy link
Contributor

apiraino commented Jun 1, 2023

WG-prioritization assigning priority (Zulip discussion).

@rustbot label -I-prioritize +P-high +T-compiler

@rustbot rustbot added P-high High priority T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. and removed I-prioritize Issue: Indicates that prioritization has been requested for this issue. labels Jun 1, 2023
@nikic nikic self-assigned this Jun 2, 2023
@nagisa
Copy link
Member

nagisa commented Jun 5, 2023

Is this a regression (from-stable-to-stable?)

@cbeuw
Copy link
Contributor Author

cbeuw commented Jun 5, 2023

@nagisa Since the Rust reproduction is in custom MIR, it's hard to test using older nightlies as a lot of expressions are only parsable recently. But on the LLVM side the bug existed since LLVM 9: https://godbolt.org/z/bvGWdvfvn, which Rust started using from 1.38.0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-LLVM Area: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues. I-unsound Issue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/Soundness P-high High priority T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants