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? # for a free GitHub account to open an issue and contact its maintainers and the community.

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

Already on GitHub? # 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.

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

No branches or pull requests

2 participants