Skip to content

Commit 99b5e76

Browse files
committed
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.
1 parent 5652b75 commit 99b5e76

File tree

2 files changed

+82
-68
lines changed

2 files changed

+82
-68
lines changed

src/librustc_mir/build/matches/mod.rs

+42-35
Original file line numberDiff line numberDiff line change
@@ -1200,51 +1200,58 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
12001200
debug!("tested_candidates: {}", total_candidate_count - candidates.len());
12011201
debug!("untested_candidates: {}", candidates.len());
12021202

1203-
// For each outcome of test, process the candidates that still
1204-
// apply. Collect a list of blocks where control flow will
1205-
// branch if one of the `target_candidate` sets is not
1206-
// exhaustive.
1207-
if !candidates.is_empty() {
1208-
let remainder_start = &mut None;
1209-
self.match_candidates(
1210-
span,
1211-
remainder_start,
1212-
otherwise_block,
1213-
candidates,
1214-
fake_borrows,
1215-
);
1216-
otherwise_block = Some(remainder_start.unwrap());
1217-
};
1218-
let target_blocks: Vec<_> = target_candidates.into_iter().map(|mut candidates| {
1219-
if candidates.len() != 0 {
1220-
let candidate_start = &mut None;
1221-
self.match_candidates(
1203+
// HACK(matthewjasper) This is a closure so that we can let the test
1204+
// create its blocks before the rest of the match. This currently
1205+
// improves the speed of llvm when optimizing long string literal
1206+
// matches
1207+
let make_target_blocks = move |this: &mut Self| -> Vec<BasicBlock> {
1208+
// For each outcome of test, process the candidates that still
1209+
// apply. Collect a list of blocks where control flow will
1210+
// branch if one of the `target_candidate` sets is not
1211+
// exhaustive.
1212+
if !candidates.is_empty() {
1213+
let remainder_start = &mut None;
1214+
this.match_candidates(
12221215
span,
1223-
candidate_start,
1216+
remainder_start,
12241217
otherwise_block,
1225-
&mut *candidates,
1218+
candidates,
12261219
fake_borrows,
12271220
);
1228-
candidate_start.unwrap()
1229-
} else {
1230-
*otherwise_block.get_or_insert_with(|| {
1231-
let unreachable = self.cfg.start_new_block();
1232-
let source_info = self.source_info(span);
1233-
self.cfg.terminate(
1234-
unreachable,
1235-
source_info,
1236-
TerminatorKind::Unreachable,
1221+
otherwise_block = Some(remainder_start.unwrap());
1222+
};
1223+
1224+
target_candidates.into_iter().map(|mut candidates| {
1225+
if candidates.len() != 0 {
1226+
let candidate_start = &mut None;
1227+
this.match_candidates(
1228+
span,
1229+
candidate_start,
1230+
otherwise_block,
1231+
&mut *candidates,
1232+
fake_borrows,
12371233
);
1238-
unreachable
1239-
})
1240-
}
1241-
}).collect();
1234+
candidate_start.unwrap()
1235+
} else {
1236+
*otherwise_block.get_or_insert_with(|| {
1237+
let unreachable = this.cfg.start_new_block();
1238+
let source_info = this.source_info(span);
1239+
this.cfg.terminate(
1240+
unreachable,
1241+
source_info,
1242+
TerminatorKind::Unreachable,
1243+
);
1244+
unreachable
1245+
})
1246+
}
1247+
}).collect()
1248+
};
12421249

12431250
self.perform_test(
12441251
block,
12451252
&match_place,
12461253
&test,
1247-
target_blocks,
1254+
make_target_blocks,
12481255
);
12491256
}
12501257

src/librustc_mir/build/matches/test.rs

+40-33
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
168168
block: BasicBlock,
169169
place: &Place<'tcx>,
170170
test: &Test<'tcx>,
171-
target_blocks: Vec<BasicBlock>,
171+
make_target_blocks: impl FnOnce(&mut Self) -> Vec<BasicBlock>,
172172
) {
173173
debug!("perform_test({:?}, {:?}: {:?}, {:?})",
174174
block,
@@ -179,6 +179,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
179179
let source_info = self.source_info(test.span);
180180
match test.kind {
181181
TestKind::Switch { adt_def, ref variants } => {
182+
let target_blocks = make_target_blocks(self);
182183
// Variants is a BitVec of indexes into adt_def.variants.
183184
let num_enum_variants = adt_def.variants.len();
184185
let used_variants = variants.count();
@@ -223,6 +224,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
223224
}
224225

225226
TestKind::SwitchInt { switch_ty, ref options, indices: _ } => {
227+
let target_blocks = make_target_blocks(self);
226228
let terminator = if switch_ty.sty == ty::Bool {
227229
assert!(options.len() > 0 && options.len() <= 2);
228230
if let [first_bb, second_bb] = *target_blocks {
@@ -254,38 +256,38 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
254256
}
255257

256258
TestKind::Eq { value, ty } => {
257-
if let [success, fail] = *target_blocks {
258-
if !ty.is_scalar() {
259-
// Use `PartialEq::eq` instead of `BinOp::Eq`
260-
// (the binop can only handle primitives)
261-
self.non_scalar_compare(
262-
block,
263-
success,
264-
fail,
265-
source_info,
266-
value,
267-
place,
268-
ty,
269-
);
270-
} else {
259+
if !ty.is_scalar() {
260+
// Use `PartialEq::eq` instead of `BinOp::Eq`
261+
// (the binop can only handle primitives)
262+
self.non_scalar_compare(
263+
block,
264+
make_target_blocks,
265+
source_info,
266+
value,
267+
place,
268+
ty,
269+
);
270+
} else {
271+
if let [success, fail] = *make_target_blocks(self) {
271272
let val = Operand::Copy(place.clone());
272273
let expect = self.literal_operand(test.span, ty, value);
273274
self.compare(block, success, fail, source_info, BinOp::Eq, expect, val);
275+
} else {
276+
bug!("`TestKind::Eq` should have two target blocks");
274277
}
275-
} else {
276-
bug!("`TestKind::Eq` should have two target blocks")
277-
};
278+
}
278279
}
279280

280281
TestKind::Range(PatternRange { ref lo, ref hi, ty, ref end }) => {
282+
let lower_bound_success = self.cfg.start_new_block();
283+
let target_blocks = make_target_blocks(self);
284+
281285
// Test `val` by computing `lo <= val && val <= hi`, using primitive comparisons.
282286
let lo = self.literal_operand(test.span, ty, lo);
283287
let hi = self.literal_operand(test.span, ty, hi);
284288
let val = Operand::Copy(place.clone());
285289

286290
if let [success, fail] = *target_blocks {
287-
let lower_bound_success = self.cfg.start_new_block();
288-
289291
self.compare(
290292
block,
291293
lower_bound_success,
@@ -306,6 +308,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
306308
}
307309

308310
TestKind::Len { len, op } => {
311+
let target_blocks = make_target_blocks(self);
312+
309313
let usize_ty = self.hir.usize_ty();
310314
let actual = self.temp(usize_ty, test.span);
311315

@@ -374,8 +378,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
374378
fn non_scalar_compare(
375379
&mut self,
376380
block: BasicBlock,
377-
success_block: BasicBlock,
378-
fail_block: BasicBlock,
381+
make_target_blocks: impl FnOnce(&mut Self) -> Vec<BasicBlock>,
379382
source_info: SourceInfo,
380383
value: &'tcx ty::Const<'tcx>,
381384
place: &Place<'tcx>,
@@ -461,17 +464,21 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
461464
from_hir_call: false,
462465
});
463466

464-
// check the result
465-
self.cfg.terminate(
466-
eq_block,
467-
source_info,
468-
TerminatorKind::if_(
469-
self.hir.tcx(),
470-
Operand::Move(eq_result),
471-
success_block,
472-
fail_block,
473-
),
474-
);
467+
if let [success_block, fail_block] = *make_target_blocks(self) {
468+
// check the result
469+
self.cfg.terminate(
470+
eq_block,
471+
source_info,
472+
TerminatorKind::if_(
473+
self.hir.tcx(),
474+
Operand::Move(eq_result),
475+
success_block,
476+
fail_block,
477+
),
478+
);
479+
} else {
480+
bug!("`TestKind::Eq` should have two target blocks")
481+
}
475482
}
476483

477484
/// Given that we are performing `test` against `test_place`, this job

0 commit comments

Comments
 (0)