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:
parent
ef1fc86b52
commit
a1d0266878
@ -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,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user