about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/librustc_mir/build/matches/mod.rs77
-rw-r--r--src/librustc_mir/build/matches/test.rs73
2 files changed, 82 insertions, 68 deletions
diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs
index 521aca56108..db4f9e55df6 100644
--- a/src/librustc_mir/build/matches/mod.rs
+++ b/src/librustc_mir/build/matches/mod.rs
@@ -1198,51 +1198,58 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
         debug!("tested_candidates: {}", total_candidate_count - candidates.len());
         debug!("untested_candidates: {}", candidates.len());
 
-        // For each outcome of test, process the candidates that still
-        // apply. Collect a list of blocks where control flow will
-        // branch if one of the `target_candidate` sets is not
-        // exhaustive.
-        if !candidates.is_empty() {
-            let remainder_start = &mut None;
-            self.match_candidates(
-                span,
-                remainder_start,
-                otherwise_block,
-                candidates,
-                fake_borrows,
-            );
-            otherwise_block = Some(remainder_start.unwrap());
-        };
-        let target_blocks: Vec<_> = target_candidates.into_iter().map(|mut candidates| {
-            if candidates.len() != 0 {
-                let candidate_start = &mut None;
-                self.match_candidates(
+        // 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
+            // apply. Collect a list of blocks where control flow will
+            // branch if one of the `target_candidate` sets is not
+            // exhaustive.
+            if !candidates.is_empty() {
+                let remainder_start = &mut None;
+                this.match_candidates(
                     span,
-                    candidate_start,
+                    remainder_start,
                     otherwise_block,
-                    &mut *candidates,
+                    candidates,
                     fake_borrows,
                 );
-                candidate_start.unwrap()
-            } else {
-                *otherwise_block.get_or_insert_with(|| {
-                    let unreachable = self.cfg.start_new_block();
-                    let source_info = self.source_info(span);
-                    self.cfg.terminate(
-                        unreachable,
-                        source_info,
-                        TerminatorKind::Unreachable,
+                otherwise_block = Some(remainder_start.unwrap());
+            };
+
+            target_candidates.into_iter().map(|mut candidates| {
+                if candidates.len() != 0 {
+                    let candidate_start = &mut None;
+                    this.match_candidates(
+                        span,
+                        candidate_start,
+                        otherwise_block,
+                        &mut *candidates,
+                        fake_borrows,
                     );
-                    unreachable
-                })
-            }
-        }).collect();
+                    candidate_start.unwrap()
+                } else {
+                    *otherwise_block.get_or_insert_with(|| {
+                        let unreachable = this.cfg.start_new_block();
+                        let source_info = this.source_info(span);
+                        this.cfg.terminate(
+                            unreachable,
+                            source_info,
+                            TerminatorKind::Unreachable,
+                        );
+                        unreachable
+                    })
+                }
+            }).collect()
+        };
 
         self.perform_test(
             block,
             &match_place,
             &test,
-            target_blocks,
+            make_target_blocks,
         );
     }
 
diff --git a/src/librustc_mir/build/matches/test.rs b/src/librustc_mir/build/matches/test.rs
index 47f0aa82a41..f93ca4b31b3 100644
--- a/src/librustc_mir/build/matches/test.rs
+++ b/src/librustc_mir/build/matches/test.rs
@@ -168,7 +168,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
         block: BasicBlock,
         place: &Place<'tcx>,
         test: &Test<'tcx>,
-        target_blocks: Vec<BasicBlock>,
+        make_target_blocks: impl FnOnce(&mut Self) -> Vec<BasicBlock>,
     ) {
         debug!("perform_test({:?}, {:?}: {:?}, {:?})",
                block,
@@ -179,6 +179,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
         let source_info = self.source_info(test.span);
         match test.kind {
             TestKind::Switch { adt_def, ref variants } => {
+                let target_blocks = make_target_blocks(self);
                 // Variants is a BitVec of indexes into adt_def.variants.
                 let num_enum_variants = adt_def.variants.len();
                 let used_variants = variants.count();
@@ -223,6 +224,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
             }
 
             TestKind::SwitchInt { switch_ty, ref options, indices: _ } => {
+                let target_blocks = make_target_blocks(self);
                 let terminator = if switch_ty.sty == ty::Bool {
                     assert!(options.len() > 0 && options.len() <= 2);
                     if let [first_bb, second_bb] = *target_blocks {
@@ -254,38 +256,38 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
             }
 
             TestKind::Eq { value, ty } => {
-                if let [success, fail] = *target_blocks {
-                    if !ty.is_scalar() {
-                        // Use `PartialEq::eq` instead of `BinOp::Eq`
-                        // (the binop can only handle primitives)
-                        self.non_scalar_compare(
-                            block,
-                            success,
-                            fail,
-                            source_info,
-                            value,
-                            place,
-                            ty,
-                        );
-                    } else {
+                if !ty.is_scalar() {
+                    // Use `PartialEq::eq` instead of `BinOp::Eq`
+                    // (the binop can only handle primitives)
+                    self.non_scalar_compare(
+                        block,
+                        make_target_blocks,
+                        source_info,
+                        value,
+                        place,
+                        ty,
+                    );
+                } else {
+                    if let [success, fail] = *make_target_blocks(self) {
                         let val = Operand::Copy(place.clone());
                         let expect = self.literal_operand(test.span, ty, value);
                         self.compare(block, success, fail, source_info, BinOp::Eq, expect, val);
+                    } else {
+                        bug!("`TestKind::Eq` should have two target blocks");
                     }
-                } else {
-                    bug!("`TestKind::Eq` should have two target blocks")
-                };
+                }
             }
 
             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.
                 let lo = self.literal_operand(test.span, ty, lo);
                 let hi = self.literal_operand(test.span, ty, hi);
                 let val = Operand::Copy(place.clone());
 
                 if let [success, fail] = *target_blocks {
-                    let lower_bound_success = self.cfg.start_new_block();
-
                     self.compare(
                         block,
                         lower_bound_success,
@@ -306,6 +308,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
             }
 
             TestKind::Len { len, op } => {
+                let target_blocks = make_target_blocks(self);
+
                 let usize_ty = self.hir.usize_ty();
                 let actual = self.temp(usize_ty, test.span);
 
@@ -374,8 +378,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
     fn non_scalar_compare(
         &mut self,
         block: BasicBlock,
-        success_block: BasicBlock,
-        fail_block: BasicBlock,
+        make_target_blocks: impl FnOnce(&mut Self) -> Vec<BasicBlock>,
         source_info: SourceInfo,
         value: &'tcx ty::Const<'tcx>,
         place: &Place<'tcx>,
@@ -461,17 +464,21 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
             from_hir_call: false,
         });
 
-        // check the result
-        self.cfg.terminate(
-            eq_block,
-            source_info,
-            TerminatorKind::if_(
-                self.hir.tcx(),
-                Operand::Move(eq_result),
-                success_block,
-                fail_block,
-            ),
-        );
+        if let [success_block, fail_block] = *make_target_blocks(self) {
+            // check the result
+            self.cfg.terminate(
+                eq_block,
+                source_info,
+                TerminatorKind::if_(
+                    self.hir.tcx(),
+                    Operand::Move(eq_result),
+                    success_block,
+                    fail_block,
+                ),
+            );
+        } else {
+            bug!("`TestKind::Eq` should have two target blocks")
+        }
     }
 
     /// Given that we are performing `test` against `test_place`, this job