Skip to content

Rework and optimize match MIR generation #136123

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 13 commits into from
5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -93,3 +93,8 @@ codegen-units = 1
# FIXME: LTO cannot be enabled for binaries in a workspace
# <https://github.com/rust-lang/cargo/issues/9330>
# lto = true

[profile.release.package.rustc_mir_build]
opt-level = 1
[profile.release.package.rustc_driver]
opt-level = 1
4 changes: 4 additions & 0 deletions compiler/rustc_const_eval/src/check_consts/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -809,6 +809,10 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
// Otherwise, it's really misleading to call something "conditionally"
// const when it's very obviously not conditionally const.
if trait_is_const && has_const_conditions == Some(ConstConditionsHold::Yes) {
if tcx.is_lang_item(trait_did, LangItem::PatternConstEq) {
return;
}

// Trait calls are always conditionally-const.
self.check_op(ops::ConditionallyConstCall {
callee,
Expand Down
5 changes: 5 additions & 0 deletions compiler/rustc_hir/src/lang_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,11 @@ language_item_table! {

String, sym::String, string, Target::Struct, GenericRequirement::None;
CStr, sym::CStr, c_str, Target::Struct, GenericRequirement::None;

// FIXME(xacrimon): Used for lowering of match/if let statements. will made obsolete by const PartialEq.
PatternConstEq, sym::PatternConstEq, pattern_const_eq, Target::Trait, GenericRequirement::None;
AggregateRawPtr, sym::aggregate_raw_ptr, aggregate_raw_ptr, Target::Fn, GenericRequirement::None;
Offset, sym::offset, offset, Target::Fn, GenericRequirement::None;
}

pub enum GenericRequirement {
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_middle/src/arena.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ macro_rules! arena_types {
[decode] specialization_graph: rustc_middle::traits::specialization_graph::Graph,
[] crate_inherent_impls: rustc_middle::ty::CrateInherentImpls,
[] hir_owner_nodes: rustc_hir::OwnerNodes<'tcx>,
[] thir_pat: rustc_middle::thir::Pat<'tcx>,
]);
)
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_middle/src/mir/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,7 @@ impl<'tcx> Const<'tcx> {
Self::Val(cv, ty)
}

#[inline]
pub fn from_usize(tcx: TyCtxt<'tcx>, n: u64) -> Self {
let ty = tcx.types.usize;
let typing_env = ty::TypingEnv::fully_monomorphized();
Expand Down
7 changes: 7 additions & 0 deletions compiler/rustc_middle/src/ty/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1555,6 +1555,13 @@ impl<'tcx> Ty<'tcx> {
ty
}

pub fn pointee(self) -> Ty<'tcx> {
match *self.kind() {
ty::RawPtr(ty, _) => ty,
_ => bug!("pointee called on non-pointer type: {:?}", self),
}
}

// FIXME(compiler-errors): Think about removing this.
#[inline]
pub fn outer_exclusive_binder(self) -> ty::DebruijnIndex {
Expand Down
194 changes: 177 additions & 17 deletions compiler/rustc_mir_build/src/builder/matches/match_pair.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
use std::ops;

use either::Either;
use rustc_middle::mir::*;
use rustc_middle::thir::{self, *};
use rustc_middle::ty::{self, Ty, TypeVisitableExt};

use crate::builder::Builder;
use crate::builder::expr::as_place::{PlaceBase, PlaceBuilder};
use crate::builder::matches::util::Range;
use crate::builder::matches::{FlatPat, MatchPairTree, TestCase};

impl<'a, 'tcx> Builder<'a, 'tcx> {
Expand Down Expand Up @@ -33,6 +37,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
/// Used internally by [`MatchPairTree::for_pattern`].
fn prefix_slice_suffix<'pat>(
&mut self,
top_pattern: &'pat Pat<'tcx>,
match_pairs: &mut Vec<MatchPairTree<'pat, 'tcx>>,
place: &PlaceBuilder<'tcx>,
prefix: &'pat [Box<Pat<'tcx>>],
Expand All @@ -54,11 +59,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
((prefix.len() + suffix.len()).try_into().unwrap(), false)
};

match_pairs.extend(prefix.iter().enumerate().map(|(idx, subpattern)| {
let elem =
ProjectionElem::ConstantIndex { offset: idx as u64, min_length, from_end: false };
MatchPairTree::for_pattern(place.clone_project(elem), subpattern, self)
}));
if !prefix.is_empty() {
let bounds = Range::from_start(0..prefix.len() as u64);
let subpattern = bounds.apply(prefix);
for pair in self.build_slice_branch(bounds, place, top_pattern, subpattern) {
match_pairs.push(pair);
}
}

if let Some(subslice_pat) = opt_slice {
let suffix_len = suffix.len() as u64;
Expand All @@ -70,16 +77,155 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
match_pairs.push(MatchPairTree::for_pattern(subslice, subslice_pat, self));
}

match_pairs.extend(suffix.iter().rev().enumerate().map(|(idx, subpattern)| {
let end_offset = (idx + 1) as u64;
let elem = ProjectionElem::ConstantIndex {
offset: if exact_size { min_length - end_offset } else { end_offset },
min_length,
from_end: !exact_size,
if !suffix.is_empty() {
let bounds = Range::from_end(0..suffix.len() as u64);
let subpattern = bounds.apply(suffix);
for pair in self.build_slice_branch(bounds, place, top_pattern, subpattern) {
match_pairs.push(pair);
}
}
}

fn build_slice_branch<'pat, 'b>(
&'b mut self,
bounds: Range,
place: &'b PlaceBuilder<'tcx>,
top_pattern: &'pat Pat<'tcx>,
pattern: &'pat [Box<Pat<'tcx>>],
) -> impl Iterator<Item = MatchPairTree<'pat, 'tcx>> + use<'a, 'tcx, 'pat, 'b> {
let entries = self.find_const_groups(pattern);

entries.into_iter().map(move |entry| {
let mut build_single = |idx| {
let subpattern = &pattern[idx as usize];
let place = place.clone_project(ProjectionElem::ConstantIndex {
offset: bounds.shift_idx(idx),
min_length: pattern.len() as u64,
from_end: bounds.from_end,
});

MatchPairTree::for_pattern(place, subpattern, self)
};
let place = place.clone_project(elem);
MatchPairTree::for_pattern(place, subpattern, self)
}));

match entry {
Either::Right(range) if range.end - range.start > 1 => {
assert!(
(range.start..range.end)
.all(|idx| self.is_constant_pattern(&pattern[idx as usize]))
);

let subpattern = &pattern[range.start as usize..range.end as usize];
let elem_ty = subpattern[0].ty;

let valtree = self.simplify_const_pattern_slice_into_valtree(subpattern);
self.valtree_to_match_pair(
top_pattern,
valtree,
place.clone(),
elem_ty,
bounds.shift_range(range),
true, // TODO: set false if only branch and only entry
)
}
Either::Right(range) => {
let tree = build_single(range.start);
assert!(self.is_constant_pattern(&pattern[range.start as usize]));
tree
}
Either::Left(idx) => build_single(idx),
}
})
}

fn find_const_groups(&self, pattern: &[Box<Pat<'tcx>>]) -> Vec<Either<u64, ops::Range<u64>>> {
let mut entries = Vec::new();
let mut current_seq_start = None;

for (idx, pat) in pattern.iter().enumerate() {
if self.is_constant_pattern(pat) {
if current_seq_start.is_none() {
current_seq_start = Some(idx as u64);
} else {
continue;
}
} else {
if let Some(start) = current_seq_start {
entries.push(Either::Right(start..idx as u64));
current_seq_start = None;
}
entries.push(Either::Left(idx as u64));
}
}

if let Some(start) = current_seq_start {
entries.push(Either::Right(start..pattern.len() as u64));
}

entries
}

fn is_constant_pattern(&self, pat: &Pat<'tcx>) -> bool {
if let PatKind::Constant { value } = pat.kind
&& let Const::Ty(_, const_) = value
&& let ty::ConstKind::Value(_, valtree) = const_.kind()
&& let ty::ValTree::Leaf(_) = valtree
{
true
} else {
false
}
}

fn extract_leaf(&self, pat: &Pat<'tcx>) -> ty::ValTree<'tcx> {
if let PatKind::Constant { value } = pat.kind
&& let Const::Ty(_, const_) = value
&& let ty::ConstKind::Value(_, valtree) = const_.kind()
&& matches!(valtree, ty::ValTree::Leaf(_))
{
valtree
} else {
unreachable!()
}
}

fn simplify_const_pattern_slice_into_valtree(
&self,
subslice: &[Box<Pat<'tcx>>],
) -> ty::ValTree<'tcx> {
let leaves = subslice.iter().map(|p| self.extract_leaf(p));
let interned = self.tcx.arena.alloc_from_iter(leaves);
ty::ValTree::Branch(interned)
}

fn valtree_to_match_pair<'pat>(
&mut self,
source_pattern: &'pat Pat<'tcx>,
valtree: ty::ValTree<'tcx>,
place: PlaceBuilder<'tcx>,
elem_ty: Ty<'tcx>,
range: Range,
do_slice: bool,
) -> MatchPairTree<'pat, 'tcx> {
let tcx = self.tcx;
let const_ty =
Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased, Ty::new_array(tcx, elem_ty, range.len()));

let pat_ty = if do_slice { Ty::new_slice(tcx, elem_ty) } else { source_pattern.ty };
let ty_const = ty::Const::new(tcx, ty::ConstKind::Value(const_ty, valtree));
let value = Const::Ty(const_ty, ty_const);
let test_case = TestCase::Constant { value, range: do_slice.then_some(range) };
let pattern = tcx.arena.alloc(Pat {
ty: pat_ty,
span: source_pattern.span,
kind: PatKind::Constant { value },
});

MatchPairTree {
place: Some(place.to_place(self)),
test_case,
subpairs: Vec::new(),
pattern,
}
}
}

Expand Down Expand Up @@ -129,7 +275,7 @@ impl<'pat, 'tcx> MatchPairTree<'pat, 'tcx> {
}
}

PatKind::Constant { value } => TestCase::Constant { value },
PatKind::Constant { value } => TestCase::Constant { value, range: None },

PatKind::AscribeUserType {
ascription: thir::Ascription { ref annotation, variance },
Expand Down Expand Up @@ -192,11 +338,25 @@ impl<'pat, 'tcx> MatchPairTree<'pat, 'tcx> {
}

PatKind::Array { ref prefix, ref slice, ref suffix } => {
cx.prefix_slice_suffix(&mut subpairs, &place_builder, prefix, slice, suffix);
cx.prefix_slice_suffix(
pattern,
&mut subpairs,
&place_builder,
prefix,
slice,
suffix,
);
default_irrefutable()
}
PatKind::Slice { ref prefix, ref slice, ref suffix } => {
cx.prefix_slice_suffix(&mut subpairs, &place_builder, prefix, slice, suffix);
cx.prefix_slice_suffix(
pattern,
&mut subpairs,
&place_builder,
prefix,
slice,
suffix,
);

if prefix.is_empty() && slice.is_some() && suffix.is_empty() {
default_irrefutable()
Expand Down
4 changes: 3 additions & 1 deletion compiler/rustc_mir_build/src/builder/matches/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ use tracing::{debug, instrument};

use crate::builder::ForGuard::{self, OutsideGuard, RefWithinGuard};
use crate::builder::expr::as_place::PlaceBuilder;
use crate::builder::matches::util::Range;
use crate::builder::scope::DropKind;
use crate::builder::{
BlockAnd, BlockAndExtension, Builder, GuardFrame, GuardFrameLocal, LocalsForNode,
Expand Down Expand Up @@ -1237,7 +1238,7 @@ struct Ascription<'tcx> {
enum TestCase<'pat, 'tcx> {
Irrefutable { binding: Option<Binding<'tcx>>, ascription: Option<Ascription<'tcx>> },
Variant { adt_def: ty::AdtDef<'tcx>, variant_index: VariantIdx },
Constant { value: mir::Const<'tcx> },
Constant { value: mir::Const<'tcx>, range: Option<Range> },
Range(&'pat PatRange<'tcx>),
Slice { len: usize, variable_length: bool },
Deref { temp: Place<'tcx>, mutability: Mutability },
Expand Down Expand Up @@ -1313,6 +1314,7 @@ enum TestKind<'tcx> {
/// `ty`,
Eq {
value: Const<'tcx>,
range: Option<Range>,
// Integer types are handled by `SwitchInt`, and constants with ADT
// types are converted back into patterns, so this can only be `&str`,
// `&[T]`, `f32` or `f64`.
Expand Down
Loading
Loading