Skip to content

Commit c656f87

Browse files
committed
Export likely(), unlikely() and cold_path() in std::hint
1 parent 733616f commit c656f87

File tree

6 files changed

+359
-4
lines changed

6 files changed

+359
-4
lines changed

library/core/src/hint.rs

+140
Original file line numberDiff line numberDiff line change
@@ -511,3 +511,143 @@ pub const fn black_box<T>(dummy: T) -> T {
511511
pub const fn must_use<T>(value: T) -> T {
512512
value
513513
}
514+
515+
/// Hints to the compiler that a branch condition is likely to be true.
516+
/// Returns the value passed to it.
517+
///
518+
/// It can be used with `if` or boolean `match` expressions.
519+
///
520+
/// When used outside of a branch condition, it may still work if there is a branch close by, but
521+
/// it is not guaranteed to have any effect.
522+
///
523+
/// It can also be applied to parts of expressions, such as `likely(a) && unlikely(b)`, or to
524+
/// compound expressions, such as `likely(a && b)`. When applied to compound expressions, it has
525+
/// the following effect:
526+
/// ```text
527+
/// likely(!a) => !unlikely(a)
528+
/// likely(a && b) => likely(a) && likely(b)
529+
/// likely(a || b) => a || likely(b)
530+
/// ```
531+
///
532+
/// See also the function [`cold_path()`] which may be more appropriate for idiomatic Rust code.
533+
///
534+
/// # Examples
535+
///
536+
/// ```
537+
/// #![feature(likely_unlikely)]
538+
/// use core::hint::likely;
539+
///
540+
/// fn foo(x: i32) {
541+
/// if likely(x > 0) {
542+
/// println!("this branch is likely to be taken");
543+
/// } else {
544+
/// println!("this branch is unlikely to be taken");
545+
/// }
546+
///
547+
/// match likely(x > 0) {
548+
/// true => println!("this branch is likely to be taken"),
549+
/// false => println!("this branch is unlikely to be taken"),
550+
/// }
551+
///
552+
/// // Use outside of a branch condition. This may still work if there is a branch close by,
553+
/// // but it is not guaranteed to have any effect
554+
/// let cond = likely(x != 0);
555+
/// if cond {
556+
/// println!("this branch is likely to be taken");
557+
/// }
558+
/// }
559+
/// ```
560+
///
561+
///
562+
#[unstable(feature = "likely_unlikely", issue = "26179")]
563+
#[rustc_nounwind]
564+
#[inline(always)]
565+
pub const fn likely(b: bool) -> bool {
566+
crate::intrinsics::likely(b)
567+
}
568+
569+
/// Hints to the compiler that a branch condition is unlikely to be true.
570+
/// Returns the value passed to it.
571+
///
572+
/// It can be used with `if` or boolean `match` expressions.
573+
///
574+
/// When used outside of a branch condition, it may still work if there is a branch close by, but
575+
/// it is not guaranteed to have any effect.
576+
///
577+
/// It can also be applied to parts of expressions, such as `likely(a) && unlikely(b)`, or to
578+
/// compound expressions, such as `unlikely(a && b)`. When applied to compound expressions, it has
579+
/// the following effect:
580+
/// ```text
581+
/// unlikely(!a) => !likely(a)
582+
/// unlikely(a && b) => a && unlikely(b)
583+
/// unlikely(a || b) => unlikely(a) || unlikely(b)
584+
/// ```
585+
///
586+
/// See also the function [`cold_path()`] which may be more appropriate for idiomatic Rust code.
587+
///
588+
/// # Examples
589+
///
590+
/// ```
591+
/// #![feature(likely_unlikely)]
592+
/// use core::hint::unlikely;
593+
///
594+
/// fn foo(x: i32) {
595+
/// if unlikely(x > 0) {
596+
/// println!("this branch is unlikely to be taken");
597+
/// } else {
598+
/// println!("this branch is likely to be taken");
599+
/// }
600+
///
601+
/// match unlikely(x > 0) {
602+
/// true => println!("this branch is unlikely to be taken"),
603+
/// false => println!("this branch is likely to be taken"),
604+
/// }
605+
///
606+
/// // Use outside of a branch condition. This may still work if there is a branch close by,
607+
/// // but it is not guaranteed to have any effect
608+
/// let cond = unlikely(x != 0);
609+
/// if cond {
610+
/// println!("this branch is likely to be taken");
611+
/// }
612+
/// }
613+
/// ```
614+
#[unstable(feature = "likely_unlikely", issue = "26179")]
615+
#[rustc_nounwind]
616+
#[inline(always)]
617+
pub const fn unlikely(b: bool) -> bool {
618+
crate::intrinsics::unlikely(b)
619+
}
620+
621+
/// Hints to the compiler that given path is cold, i.e., unlikely to be taken. The compiler may
622+
/// choose to optimize paths that are not cold at the expense of paths that are cold.
623+
///
624+
/// # Examples
625+
///
626+
/// ```
627+
/// #![feature(cold_path)]
628+
/// use core::hint::cold_path;
629+
///
630+
/// fn foo(x: &[i32]) {
631+
/// if let Some(first) = x.get(0) {
632+
/// // this is the fast path
633+
/// } else {
634+
/// // this path is unlikely
635+
/// cold_path();
636+
/// }
637+
/// }
638+
///
639+
/// fn bar(x: i32) -> i32 {
640+
/// match x {
641+
/// 1 => 10,
642+
/// 2 => 100,
643+
/// 3 => { cold_path(); 1000 }, // this branch is unlikely
644+
/// _ => { cold_path(); 10000 }, // this is also unlikely
645+
/// }
646+
/// }
647+
/// ```
648+
#[unstable(feature = "cold_path", issue = "26179")]
649+
#[rustc_nounwind]
650+
#[inline(always)]
651+
pub const fn cold_path() {
652+
crate::intrinsics::cold_path()
653+
}

tests/codegen/hint/cold_path.rs

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
//@ compile-flags: -O
2+
#![crate_type = "lib"]
3+
#![feature(cold_path)]
4+
5+
use std::hint::cold_path;
6+
7+
#[inline(never)]
8+
#[no_mangle]
9+
pub fn path_a() {
10+
println!("path a");
11+
}
12+
13+
#[inline(never)]
14+
#[no_mangle]
15+
pub fn path_b() {
16+
println!("path b");
17+
}
18+
19+
#[no_mangle]
20+
pub fn test1(x: bool) {
21+
if x {
22+
path_a();
23+
} else {
24+
cold_path();
25+
path_b();
26+
}
27+
28+
// CHECK-LABEL: @test1(
29+
// CHECK: br i1 %x, label %bb1, label %bb2, !prof ![[NUM:[0-9]+]]
30+
// CHECK: bb2:
31+
// CHECK: path_b
32+
// CHECK: bb1:
33+
// CHECK: path_a
34+
}
35+
36+
#[no_mangle]
37+
pub fn test2(x: i32) {
38+
match x > 0 {
39+
true => path_a(),
40+
false => {
41+
cold_path();
42+
path_b()
43+
}
44+
}
45+
46+
// CHECK-LABEL: @test2(
47+
// CHECK: br i1 %_2, label %bb2, label %bb1, !prof ![[NUM]]
48+
// CHECK: bb1:
49+
// CHECK: path_b
50+
// CHECK: bb2:
51+
// CHECK: path_a
52+
}
53+
54+
// CHECK: ![[NUM]] = !{!"branch_weights", {{(!"expected", )?}}i32 2000, i32 1}

tests/codegen/hint/likely.rs

+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
//@ compile-flags: -O
2+
#![crate_type = "lib"]
3+
#![feature(likely_unlikely)]
4+
5+
use std::hint::likely;
6+
7+
#[inline(never)]
8+
#[no_mangle]
9+
pub fn path_a() {
10+
println!("path a");
11+
}
12+
13+
#[inline(never)]
14+
#[no_mangle]
15+
pub fn path_b() {
16+
println!("path b");
17+
}
18+
19+
#[no_mangle]
20+
pub fn test1(x: bool) {
21+
if likely(x) {
22+
path_a();
23+
} else {
24+
path_b();
25+
}
26+
27+
// CHECK-LABEL: @test1(
28+
// CHECK: br i1 %x, label %bb2, label %bb3, !prof ![[NUM:[0-9]+]]
29+
// CHECK: bb3:
30+
// CHECK: path_b
31+
// CHECK: bb2:
32+
// CHECK: path_a
33+
}
34+
35+
#[no_mangle]
36+
pub fn test2(x: i32) {
37+
match likely(x > 0) {
38+
true => path_a(),
39+
false => path_b(),
40+
}
41+
42+
// CHECK-LABEL: @test2(
43+
// CHECK: br i1 %_2, label %bb2, label %bb3, !prof ![[NUM]]
44+
// CHECK: bb3:
45+
// CHECK: path_b
46+
// CHECK: bb2:
47+
// CHECK: path_a
48+
}
49+
50+
#[no_mangle]
51+
pub fn test3(x: i8) {
52+
match likely(x < 7) {
53+
true => path_a(),
54+
_ => path_b(),
55+
}
56+
57+
// CHECK-LABEL: @test3(
58+
// CHECK: br i1 %_2, label %bb2, label %bb3, !prof ![[NUM]]
59+
// CHECK: bb3:
60+
// CHECK: path_b
61+
// CHECK: bb2:
62+
// CHECK: path_a
63+
}
64+
65+
#[no_mangle]
66+
pub fn test4(x: u64) {
67+
match likely(x != 33) {
68+
false => path_a(),
69+
_ => path_b(),
70+
}
71+
72+
// CHECK-LABEL: @test4(
73+
// CHECK: br i1 %0, label %bb3, label %bb2, !prof ![[NUM2:[0-9]+]]
74+
// CHECK: bb3:
75+
// CHECK: path_a
76+
// CHECK: bb2:
77+
// CHECK: path_b
78+
}
79+
80+
// CHECK: ![[NUM]] = !{!"branch_weights", {{(!"expected", )?}}i32 2000, i32 1}
81+
// CHECK: ![[NUM2]] = !{!"branch_weights", {{(!"expected", )?}}i32 1, i32 2000}

tests/codegen/hint/unlikely.rs

+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
//@ compile-flags: -O
2+
#![crate_type = "lib"]
3+
#![feature(likely_unlikely)]
4+
5+
use std::hint::unlikely;
6+
7+
#[inline(never)]
8+
#[no_mangle]
9+
pub fn path_a() {
10+
println!("path a");
11+
}
12+
13+
#[inline(never)]
14+
#[no_mangle]
15+
pub fn path_b() {
16+
println!("path b");
17+
}
18+
19+
#[no_mangle]
20+
pub fn test1(x: bool) {
21+
if unlikely(x) {
22+
path_a();
23+
} else {
24+
path_b();
25+
}
26+
27+
// CHECK-LABEL: @test1(
28+
// CHECK: br i1 %x, label %bb2, label %bb4, !prof ![[NUM:[0-9]+]]
29+
// CHECK: bb4:
30+
// CHECK: path_b
31+
// CHECK: bb2:
32+
// CHECK: path_a
33+
}
34+
35+
#[no_mangle]
36+
pub fn test2(x: i32) {
37+
match unlikely(x > 0) {
38+
true => path_a(),
39+
false => path_b(),
40+
}
41+
42+
// CHECK-LABEL: @test2(
43+
// CHECK: br i1 %_2, label %bb2, label %bb4, !prof ![[NUM]]
44+
// CHECK: bb4:
45+
// CHECK: path_b
46+
// CHECK: bb2:
47+
// CHECK: path_a
48+
}
49+
50+
#[no_mangle]
51+
pub fn test3(x: i8) {
52+
match unlikely(x < 7) {
53+
true => path_a(),
54+
_ => path_b(),
55+
}
56+
57+
// CHECK-LABEL: @test3(
58+
// CHECK: br i1 %_2, label %bb2, label %bb4, !prof ![[NUM]]
59+
// CHECK: bb4:
60+
// CHECK: path_b
61+
// CHECK: bb2:
62+
// CHECK: path_a
63+
}
64+
65+
#[no_mangle]
66+
pub fn test4(x: u64) {
67+
match unlikely(x != 33) {
68+
false => path_a(),
69+
_ => path_b(),
70+
}
71+
72+
// CHECK-LABEL: @test4(
73+
// CHECK: br i1 %0, label %bb4, label %bb2, !prof ![[NUM2:[0-9]+]]
74+
// CHECK: bb4:
75+
// CHECK: path_a
76+
// CHECK: bb2:
77+
// CHECK: path_b
78+
}
79+
80+
// CHECK: ![[NUM]] = !{!"branch_weights", {{(!"expected", )?}}i32 1, i32 2000}

tests/mir-opt/pre-codegen/checked_ops.step_forward.PreCodegen.after.panic-abort.mir

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ fn step_forward(_1: u16, _2: usize) -> u16 {
1313
scope 6 (inlined core::num::<impl u16>::checked_add) {
1414
let mut _5: (u16, bool);
1515
let mut _6: bool;
16-
scope 7 (inlined unlikely) {
16+
scope 7 (inlined std::intrinsics::unlikely) {
1717
let _7: ();
1818
}
1919
}
@@ -55,7 +55,7 @@ fn step_forward(_1: u16, _2: usize) -> u16 {
5555
}
5656

5757
bb3: {
58-
_7 = cold_path() -> [return: bb4, unwind unreachable];
58+
_7 = std::intrinsics::cold_path() -> [return: bb4, unwind unreachable];
5959
}
6060

6161
bb4: {

tests/mir-opt/pre-codegen/checked_ops.step_forward.PreCodegen.after.panic-unwind.mir

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ fn step_forward(_1: u16, _2: usize) -> u16 {
1313
scope 6 (inlined core::num::<impl u16>::checked_add) {
1414
let mut _5: (u16, bool);
1515
let mut _6: bool;
16-
scope 7 (inlined unlikely) {
16+
scope 7 (inlined std::intrinsics::unlikely) {
1717
let _7: ();
1818
}
1919
}
@@ -55,7 +55,7 @@ fn step_forward(_1: u16, _2: usize) -> u16 {
5555
}
5656

5757
bb3: {
58-
_7 = cold_path() -> [return: bb4, unwind unreachable];
58+
_7 = std::intrinsics::cold_path() -> [return: bb4, unwind unreachable];
5959
}
6060

6161
bb4: {

0 commit comments

Comments
 (0)