likely/unlikely intrinsics fail to propagate through inline functions

eed008a
Opened by gnzlbg at 2020-03-22 20:12:49

See it live:

#![feature(core_intrinsics)]
extern crate core;
use core::intrinsics::unlikely;

#[inline(always)]
fn test(x: bool) -> bool {
    unsafe {
        unlikely(x)
    }
}

pub fn foo(x: u32) -> u32 {
  if test(x == 0) { 1 } else { x * 2 } 
}

pub fn foo2(x: u32) -> u32 {
  unsafe {
  let c = unlikely(x == 0);
  if c { 1 } else { x * 2 } 
  }
}

pub fn bar(x: u32) -> u32 {
  if x == 0 { 1 } else { x * 2 } 
}

foo2 generates:

        push    rbp
        mov     rbp, rsp
        test    edi, edi
        je      .LBB1_1
        add     edi, edi
.LBB1_3:
        mov     eax, edi
        pop     rbp
        ret
.LBB1_1:
        mov     edi, 1
        jmp     .LBB1_3

but foo generates the same assembly as bar instead of the same assembly as foo2:

        push    rbp
        mov     rbp, rsp
        lea     ecx, [rdi + rdi]
        test    edi, edi
        mov     eax, 1
        cmovne  eax, ecx
        pop     rbp
        ret
  1. Turning on MIR inlining using -Z mir-opt-level=3 makes foo and foo2 identical. I don't know what other effects that may have though (and at the moment the MIR opt-levels that enable MIR inlining also enables MIR copy-propagation which can be very slow).

    oyvindln at 2017-10-23 19:26:19

  2. In case this is useful, I had a quick play around with --emit=llvm-ir: in debug mode, the expect intrinsic appears regardless of inlining. In release mode, without inlining it's converted to branch_weights, with inlining it's absent.

    Alec Mocatta at 2018-08-05 00:43:08