Skip to content

Commit 368ca13

Browse files
committed
Initial implementation of #[cold] attribute on match arms
1 parent 432fffa commit 368ca13

File tree

14 files changed

+210
-21
lines changed

14 files changed

+210
-21
lines changed

compiler/rustc_codegen_llvm/src/builder.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,36 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
197197
}
198198
}
199199

200+
fn cond_br_with_cold_br(
201+
&mut self,
202+
cond: &'ll Value,
203+
then_llbb: Self::BasicBlock,
204+
else_llbb: Self::BasicBlock,
205+
cold_br: Option<bool>,
206+
) {
207+
// emit the branch instruction
208+
let n = unsafe {
209+
llvm::LLVMBuildCondBr(self.llbuilder, cond, then_llbb, else_llbb)
210+
};
211+
212+
// if one of the branches is cold, emit metadata with branch weights
213+
if let Some(cold_br) = cold_br {
214+
unsafe {
215+
let s = "branch_weights";
216+
let v = [
217+
llvm::LLVMMDStringInContext(self.cx.llcx, s.as_ptr() as *const c_char, s.len() as c_uint),
218+
self.cx.const_u32(if cold_br { 1 } else { 2000 }), // 'then' branch weight
219+
self.cx.const_u32(if cold_br { 2000 } else { 1 }), // 'else' branch weight
220+
];
221+
llvm::LLVMSetMetadata(
222+
n,
223+
llvm::MD_prof as c_uint,
224+
llvm::LLVMMDNodeInContext(self.cx.llcx, v.as_ptr(), v.len() as c_uint),
225+
);
226+
}
227+
}
228+
}
229+
200230
fn switch(
201231
&mut self,
202232
v: &'ll Value,

compiler/rustc_codegen_ssa/src/mir/block.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -327,18 +327,27 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
327327
let (test_value, target) = target_iter.next().unwrap();
328328
let lltrue = helper.llbb_with_cleanup(self, target);
329329
let llfalse = helper.llbb_with_cleanup(self, targets.otherwise());
330+
let cold_br = targets.cold_target().and_then(|t| {
331+
if t == 0 { Some(true) } else { Some(false) }
332+
});
333+
330334
if switch_ty == bx.tcx().types.bool {
331335
// Don't generate trivial icmps when switching on bool.
332336
match test_value {
333-
0 => bx.cond_br(discr.immediate(), llfalse, lltrue),
334-
1 => bx.cond_br(discr.immediate(), lltrue, llfalse),
337+
0 => {
338+
let cold_br = cold_br.and_then(|t| Some(!t));
339+
bx.cond_br_with_cold_br(discr.immediate(), llfalse, lltrue, cold_br);
340+
},
341+
1 => {
342+
bx.cond_br_with_cold_br(discr.immediate(), lltrue, llfalse, cold_br)
343+
},
335344
_ => bug!(),
336345
}
337346
} else {
338347
let switch_llty = bx.immediate_backend_type(bx.layout_of(switch_ty));
339348
let llval = bx.const_uint_big(switch_llty, test_value);
340349
let cmp = bx.icmp(IntPredicate::IntEQ, discr.immediate(), llval);
341-
bx.cond_br(cmp, lltrue, llfalse);
350+
bx.cond_br_with_cold_br(cmp, lltrue, llfalse, cold_br);
342351
}
343352
} else if self.cx.sess().opts.optimize == OptLevel::No
344353
&& target_iter.len() == 2

compiler/rustc_codegen_ssa/src/traits/builder.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,13 @@ pub trait BuilderMethods<'a, 'tcx>:
6464
then_llbb: Self::BasicBlock,
6565
else_llbb: Self::BasicBlock,
6666
);
67+
fn cond_br_with_cold_br(
68+
&mut self,
69+
cond: Self::Value,
70+
then_llbb: Self::BasicBlock,
71+
else_llbb: Self::BasicBlock,
72+
cold_br: Option<bool>,
73+
);
6774
fn switch(
6875
&mut self,
6976
v: Self::Value,

compiler/rustc_data_structures/src/stable_hasher.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use crate::sip128::SipHasher128;
22
use rustc_index::bit_set::{self, BitSet};
33
use rustc_index::{Idx, IndexSlice, IndexVec};
44
use smallvec::SmallVec;
5+
use thin_vec::ThinVec;
56
use std::fmt;
67
use std::hash::{BuildHasher, Hash, Hasher};
78
use std::marker::PhantomData;
@@ -461,6 +462,16 @@ where
461462
}
462463
}
463464

465+
impl<T, CTX> HashStable<CTX> for ThinVec<T>
466+
where
467+
T: HashStable<CTX>,
468+
{
469+
#[inline]
470+
fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
471+
self[..].hash_stable(ctx, hasher);
472+
}
473+
}
474+
464475
impl<T: ?Sized + HashStable<CTX>, CTX> HashStable<CTX> for Box<T> {
465476
#[inline]
466477
fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {

compiler/rustc_middle/src/mir/syntax.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ use rustc_span::symbol::Symbol;
2424
use rustc_span::Span;
2525
use rustc_target::asm::InlineAsmRegOrRegClass;
2626
use smallvec::SmallVec;
27+
use thin_vec::ThinVec;
2728

2829
/// Represents the "flavors" of MIR.
2930
///
@@ -841,6 +842,12 @@ pub struct SwitchTargets {
841842
// However we’ve decided to keep this as-is until we figure a case
842843
// where some other approach seems to be strictly better than other.
843844
pub(super) targets: SmallVec<[BasicBlock; 2]>,
845+
846+
// Targets that are marked 'cold', if any.
847+
// This vector contains indices into `targets`.
848+
// It can also contain 'targets.len()' to indicate that the otherwise
849+
// branch is cold.
850+
pub(super) cold_targets: ThinVec<usize>,
844851
}
845852

846853
/// Action to be taken when a stack unwind happens.

compiler/rustc_middle/src/mir/terminator.rs

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use smallvec::SmallVec;
55
use super::TerminatorKind;
66
use rustc_macros::HashStable;
77
use std::slice;
8+
use thin_vec::{thin_vec, ThinVec};
89

910
use super::*;
1011

@@ -16,13 +17,36 @@ impl SwitchTargets {
1617
pub fn new(targets: impl Iterator<Item = (u128, BasicBlock)>, otherwise: BasicBlock) -> Self {
1718
let (values, mut targets): (SmallVec<_>, SmallVec<_>) = targets.unzip();
1819
targets.push(otherwise);
19-
Self { values, targets }
20+
Self { values, targets, cold_targets: ThinVec::new() }
2021
}
2122

2223
/// Builds a switch targets definition that jumps to `then` if the tested value equals `value`,
2324
/// and to `else_` if not.
2425
pub fn static_if(value: u128, then: BasicBlock, else_: BasicBlock) -> Self {
25-
Self { values: smallvec![value], targets: smallvec![then, else_] }
26+
Self {
27+
values: smallvec![value],
28+
targets: smallvec![then, else_],
29+
cold_targets: ThinVec::new(),
30+
}
31+
}
32+
33+
/// Builds a switch targets definition that jumps to `then` if the tested value equals `value`,
34+
/// and to `else_` if not.
35+
/// If cold_br is some bool value, the given outcome is considered cold (i.e., unlikely).
36+
pub fn static_if_with_cold_br(
37+
value: u128,
38+
then: BasicBlock,
39+
else_: BasicBlock,
40+
cold_br: Option<bool>,
41+
) -> Self {
42+
Self {
43+
values: smallvec![value],
44+
targets: smallvec![then, else_],
45+
cold_targets: match cold_br {
46+
Some(br) => thin_vec![if br { 0 } else { 1 }],
47+
None => ThinVec::new(),
48+
},
49+
}
2650
}
2751

2852
/// Inverse of `SwitchTargets::static_if`.
@@ -36,6 +60,14 @@ impl SwitchTargets {
3660
}
3761
}
3862

63+
pub fn cold_target(&self) -> Option<usize> {
64+
if self.cold_targets.len() == 1 {
65+
Some(self.cold_targets[0])
66+
} else {
67+
None
68+
}
69+
}
70+
3971
/// Returns the fallback target that is jumped to when none of the values match the operand.
4072
pub fn otherwise(&self) -> BasicBlock {
4173
*self.targets.last().unwrap()
@@ -352,6 +384,22 @@ impl<'tcx> TerminatorKind<'tcx> {
352384
TerminatorKind::SwitchInt { discr: cond, targets: SwitchTargets::static_if(0, f, t) }
353385
}
354386

387+
pub fn if_with_cold_br(
388+
cond: Operand<'tcx>,
389+
t: BasicBlock,
390+
f: BasicBlock,
391+
cold_branch: Option<bool>,
392+
) -> TerminatorKind<'tcx> {
393+
TerminatorKind::SwitchInt {
394+
discr: cond,
395+
targets: SwitchTargets::static_if_with_cold_br(
396+
0, f, t,
397+
// we compare to zero, so have to invert the branch
398+
cold_branch.and_then(|b| Some(!b))
399+
),
400+
}
401+
}
402+
355403
pub fn successors(&self) -> Successors<'_> {
356404
use self::TerminatorKind::*;
357405
match *self {

compiler/rustc_middle/src/thir.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -524,6 +524,7 @@ pub struct Arm<'tcx> {
524524
pub lint_level: LintLevel,
525525
pub scope: region::Scope,
526526
pub span: Span,
527+
pub is_cold: bool,
527528
}
528529

529530
/// A `match` guard.

compiler/rustc_mir_build/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,6 @@ rustc_span = { path = "../rustc_span" }
2323
rustc_target = { path = "../rustc_target" }
2424
rustc_trait_selection = { path = "../rustc_trait_selection" }
2525
smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
26+
thin-vec = "0.2.12"
2627
tracing = "0.1"
2728
# tidy-alphabetical-end

compiler/rustc_mir_build/src/build/expr/into.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
8282
cond,
8383
Some(condition_scope),
8484
condition_scope,
85-
source_info
85+
source_info,
86+
None,
8687
));
8788

8889
this.expr_into_dest(destination, then_blk, then)
@@ -173,6 +174,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
173174
Some(condition_scope),
174175
condition_scope,
175176
source_info,
177+
None,
176178
)
177179
});
178180
let (short_circuit, continuation, constant) = match op {

0 commit comments

Comments
 (0)