Generate MIR thats easier for llvm for str matches

LLVM appears to prefer (spend less time optimizing) long if chains if
it receives them in approzimately source order.
This fixes a ~10% regression for optimized builds of the encoding benchmark
on perf.rlo due to the changes to decision tree construction.
This commit is contained in:
Matthew Jasper 2019-05-25 17:11:27 +01:00
parent ef1fc86b52
commit a1d0266878
2 changed files with 82 additions and 68 deletions

View File

@ -1198,13 +1198,18 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
debug!("tested_candidates: {}", total_candidate_count - candidates.len()); debug!("tested_candidates: {}", total_candidate_count - candidates.len());
debug!("untested_candidates: {}", candidates.len()); debug!("untested_candidates: {}", candidates.len());
// HACK(matthewjasper) This is a closure so that we can let the test
// create its blocks before the rest of the match. This currently
// improves the speed of llvm when optimizing long string literal
// matches
let make_target_blocks = move |this: &mut Self| -> Vec<BasicBlock> {
// For each outcome of test, process the candidates that still // For each outcome of test, process the candidates that still
// apply. Collect a list of blocks where control flow will // apply. Collect a list of blocks where control flow will
// branch if one of the `target_candidate` sets is not // branch if one of the `target_candidate` sets is not
// exhaustive. // exhaustive.
if !candidates.is_empty() { if !candidates.is_empty() {
let remainder_start = &mut None; let remainder_start = &mut None;
self.match_candidates( this.match_candidates(
span, span,
remainder_start, remainder_start,
otherwise_block, otherwise_block,
@ -1213,10 +1218,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
); );
otherwise_block = Some(remainder_start.unwrap()); otherwise_block = Some(remainder_start.unwrap());
}; };
let target_blocks: Vec<_> = target_candidates.into_iter().map(|mut candidates| {
target_candidates.into_iter().map(|mut candidates| {
if candidates.len() != 0 { if candidates.len() != 0 {
let candidate_start = &mut None; let candidate_start = &mut None;
self.match_candidates( this.match_candidates(
span, span,
candidate_start, candidate_start,
otherwise_block, otherwise_block,
@ -1226,9 +1232,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
candidate_start.unwrap() candidate_start.unwrap()
} else { } else {
*otherwise_block.get_or_insert_with(|| { *otherwise_block.get_or_insert_with(|| {
let unreachable = self.cfg.start_new_block(); let unreachable = this.cfg.start_new_block();
let source_info = self.source_info(span); let source_info = this.source_info(span);
self.cfg.terminate( this.cfg.terminate(
unreachable, unreachable,
source_info, source_info,
TerminatorKind::Unreachable, TerminatorKind::Unreachable,
@ -1236,13 +1242,14 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
unreachable unreachable
}) })
} }
}).collect(); }).collect()
};
self.perform_test( self.perform_test(
block, block,
&match_place, &match_place,
&test, &test,
target_blocks, make_target_blocks,
); );
} }

View File

@ -168,7 +168,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
block: BasicBlock, block: BasicBlock,
place: &Place<'tcx>, place: &Place<'tcx>,
test: &Test<'tcx>, test: &Test<'tcx>,
target_blocks: Vec<BasicBlock>, make_target_blocks: impl FnOnce(&mut Self) -> Vec<BasicBlock>,
) { ) {
debug!("perform_test({:?}, {:?}: {:?}, {:?})", debug!("perform_test({:?}, {:?}: {:?}, {:?})",
block, block,
@ -179,6 +179,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
let source_info = self.source_info(test.span); let source_info = self.source_info(test.span);
match test.kind { match test.kind {
TestKind::Switch { adt_def, ref variants } => { TestKind::Switch { adt_def, ref variants } => {
let target_blocks = make_target_blocks(self);
// Variants is a BitVec of indexes into adt_def.variants. // Variants is a BitVec of indexes into adt_def.variants.
let num_enum_variants = adt_def.variants.len(); let num_enum_variants = adt_def.variants.len();
let used_variants = variants.count(); let used_variants = variants.count();
@ -223,6 +224,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
} }
TestKind::SwitchInt { switch_ty, ref options, indices: _ } => { TestKind::SwitchInt { switch_ty, ref options, indices: _ } => {
let target_blocks = make_target_blocks(self);
let terminator = if switch_ty.sty == ty::Bool { let terminator = if switch_ty.sty == ty::Bool {
assert!(options.len() > 0 && options.len() <= 2); assert!(options.len() > 0 && options.len() <= 2);
if let [first_bb, second_bb] = *target_blocks { if let [first_bb, second_bb] = *target_blocks {
@ -254,38 +256,38 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
} }
TestKind::Eq { value, ty } => { TestKind::Eq { value, ty } => {
if let [success, fail] = *target_blocks {
if !ty.is_scalar() { if !ty.is_scalar() {
// Use `PartialEq::eq` instead of `BinOp::Eq` // Use `PartialEq::eq` instead of `BinOp::Eq`
// (the binop can only handle primitives) // (the binop can only handle primitives)
self.non_scalar_compare( self.non_scalar_compare(
block, block,
success, make_target_blocks,
fail,
source_info, source_info,
value, value,
place, place,
ty, ty,
); );
} else { } else {
if let [success, fail] = *make_target_blocks(self) {
let val = Operand::Copy(place.clone()); let val = Operand::Copy(place.clone());
let expect = self.literal_operand(test.span, ty, value); let expect = self.literal_operand(test.span, ty, value);
self.compare(block, success, fail, source_info, BinOp::Eq, expect, val); self.compare(block, success, fail, source_info, BinOp::Eq, expect, val);
}
} else { } else {
bug!("`TestKind::Eq` should have two target blocks") bug!("`TestKind::Eq` should have two target blocks");
}; }
}
} }
TestKind::Range(PatternRange { ref lo, ref hi, ty, ref end }) => { TestKind::Range(PatternRange { ref lo, ref hi, ty, ref end }) => {
let lower_bound_success = self.cfg.start_new_block();
let target_blocks = make_target_blocks(self);
// Test `val` by computing `lo <= val && val <= hi`, using primitive comparisons. // Test `val` by computing `lo <= val && val <= hi`, using primitive comparisons.
let lo = self.literal_operand(test.span, ty, lo); let lo = self.literal_operand(test.span, ty, lo);
let hi = self.literal_operand(test.span, ty, hi); let hi = self.literal_operand(test.span, ty, hi);
let val = Operand::Copy(place.clone()); let val = Operand::Copy(place.clone());
if let [success, fail] = *target_blocks { if let [success, fail] = *target_blocks {
let lower_bound_success = self.cfg.start_new_block();
self.compare( self.compare(
block, block,
lower_bound_success, lower_bound_success,
@ -306,6 +308,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
} }
TestKind::Len { len, op } => { TestKind::Len { len, op } => {
let target_blocks = make_target_blocks(self);
let usize_ty = self.hir.usize_ty(); let usize_ty = self.hir.usize_ty();
let actual = self.temp(usize_ty, test.span); let actual = self.temp(usize_ty, test.span);
@ -374,8 +378,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
fn non_scalar_compare( fn non_scalar_compare(
&mut self, &mut self,
block: BasicBlock, block: BasicBlock,
success_block: BasicBlock, make_target_blocks: impl FnOnce(&mut Self) -> Vec<BasicBlock>,
fail_block: BasicBlock,
source_info: SourceInfo, source_info: SourceInfo,
value: &'tcx ty::Const<'tcx>, value: &'tcx ty::Const<'tcx>,
place: &Place<'tcx>, place: &Place<'tcx>,
@ -461,6 +464,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
from_hir_call: false, from_hir_call: false,
}); });
if let [success_block, fail_block] = *make_target_blocks(self) {
// check the result // check the result
self.cfg.terminate( self.cfg.terminate(
eq_block, eq_block,
@ -472,6 +476,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
fail_block, fail_block,
), ),
); );
} else {
bug!("`TestKind::Eq` should have two target blocks")
}
} }
/// Given that we are performing `test` against `test_place`, this job /// Given that we are performing `test` against `test_place`, this job