Skip to content

panics are eliminated if panic_fmt have an empty loop and extern "C" #47537

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

Closed
pepyakin opened this issue Jan 18, 2018 · 2 comments
Closed

panics are eliminated if panic_fmt have an empty loop and extern "C" #47537

pepyakin opened this issue Jan 18, 2018 · 2 comments

Comments

@pepyakin
Copy link
Contributor

pepyakin commented Jan 18, 2018

Working on example for #47526 I noticed that if loop body in panic_fmt is empty AND it is declared as extern "C" then optimizer will assume that the loop have no effects and will eliminate panic

So for this code

#![feature(lang_items)]
#![no_std]
#![no_main]

#[no_mangle]
#[lang = "panic_fmt"]
pub extern "C" fn panic_fmt(
    _args: ::core::fmt::Arguments,
    _file: &'static str,
    _line: u32,
    _col: u32,
) -> ! {
    loop {
    }
}

#[lang = "eh_personality"] extern fn eh_personality() {}

#[no_mangle]
pub fn call(descriptor: u8) -> u8 {
    assert!(descriptor > 0);
    descriptor
}

LLVM IR looks like

; Function Attrs: norecurse noreturn nounwind readnone
define void @rust_begin_unwind(%"core::fmt::Arguments"* byval noalias nocapture dereferenceable(24) %_args, { [0 x i8]*, i32 }* byval noalias nocapture dereferenceable(8) %_file, i32 %_line, i32 %_col) unnamed_addr #0 {
start:
  br label %bb1

bb1:                                              ; preds = %bb1, %start
  br label %bb1
}

; Function Attrs: norecurse nounwind readnone
define i8 @call(i8 returned %descriptor) unnamed_addr #1 {
start:
  ret i8 %descriptor
}

As you can see, assert code is eliminated. However, if I add something that has side-effects (as in original example) or remove extern "C" call will be compiled as

; Function Attrs: nounwind
define i8 @call(i8 returned %descriptor) unnamed_addr #1 {
start:
  %0 = icmp eq i8 %descriptor, 0
  br i1 %0, label %bb1, label %bb2

bb1:                                              ; preds = %start
; call core::panicking::panic
  tail call fastcc void @_ZN4core9panicking5panic17h2733bdb7358b666dE()
  unreachable

bb2:                                              ; preds = %start
  ret i8 %descriptor
}
@hanna-kruppe
Copy link
Contributor

Same root cause as #38136 (loop {} being UB, tracked in #28728). There's likely no deep reason why extern "C" makes a difference, LLVM's removal of empty loops is just very brittle (intentionally, I believe, to miscompile less real world C code).

@pepyakin
Copy link
Contributor Author

Let's close then as duplicate of #28728.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants