Skip to content

Commit b01358a

Browse files
committed
Auto merge of #120003 - Mark-Simulacrum:opt-promoted, r=<try>
perf: Don't track specific live points for promoteds We don't query this information out of the promoted (it's basically a single "unit" regardless of the complexity within it) and this saves on re-initializing the SparseIntervalMatrix's backing IndexVec with mostly empty rows for all of the leading regions in the function. Typical promoteds will only contain a few regions that need up be uplifted, while the parent function can have thousands. For a simple function repeating println!("Hello world"); 50,000 times this reduces compile times from 90 to 15 seconds in debug mode. The previous implementations re-initialization led to an overall roughly n^2 runtime as each promoted initialized slots for ~n regions, now we scale closer to linearly (5000 hello worlds takes 1.1 seconds). cc #50994, #86244
2 parents fa0dc20 + 6785738 commit b01358a

File tree

2 files changed

+93
-31
lines changed

2 files changed

+93
-31
lines changed

compiler/rustc_borrowck/src/region_infer/values.rs

+72-17
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#![deny(rustc::untranslatable_diagnostic)]
22
#![deny(rustc::diagnostic_outside_of_impl)]
3+
use rustc_data_structures::fx::FxHashSet;
34
use rustc_data_structures::fx::FxIndexSet;
45
use rustc_index::bit_set::SparseBitMatrix;
56
use rustc_index::interval::IntervalSet;
@@ -125,8 +126,15 @@ pub(crate) struct LivenessValues {
125126
/// The map from locations to points.
126127
elements: Rc<RegionValueElements>,
127128

129+
/// Which regions are live. This is exclusive with the fine-grained tracking in `points`, and
130+
/// currently only used for validating promoteds (which don't care about more precise tracking).
131+
live_regions: Option<FxHashSet<RegionVid>>,
132+
128133
/// For each region: the points where it is live.
129-
points: SparseIntervalMatrix<RegionVid, PointIndex>,
134+
///
135+
/// This is not initialized for promoteds, because we don't care *where* within a promoted a
136+
/// region is live, only that it is.
137+
points: Option<SparseIntervalMatrix<RegionVid, PointIndex>>,
130138

131139
/// When using `-Zpolonius=next`, for each point: the loans flowing into the live regions at
132140
/// that point.
@@ -155,24 +163,52 @@ impl LiveLoans {
155163

156164
impl LivenessValues {
157165
/// Create an empty map of regions to locations where they're live.
158-
pub(crate) fn new(elements: Rc<RegionValueElements>) -> Self {
166+
pub(crate) fn with_specific_points(elements: Rc<RegionValueElements>) -> Self {
159167
LivenessValues {
160-
points: SparseIntervalMatrix::new(elements.num_points),
168+
live_regions: None,
169+
points: Some(SparseIntervalMatrix::new(elements.num_points)),
170+
elements,
171+
loans: None,
172+
}
173+
}
174+
175+
/// Create an empty map of regions to locations where they're live.
176+
///
177+
/// Unlike `with_specific_points`, does not track exact locations where something is live, only
178+
/// which regions are live.
179+
pub(crate) fn without_specific_points(elements: Rc<RegionValueElements>) -> Self {
180+
LivenessValues {
181+
live_regions: Some(Default::default()),
182+
points: None,
161183
elements,
162184
loans: None,
163185
}
164186
}
165187

166188
/// Iterate through each region that has a value in this set.
167-
pub(crate) fn regions(&self) -> impl Iterator<Item = RegionVid> {
168-
self.points.rows()
189+
pub(crate) fn regions(&self) -> impl Iterator<Item = RegionVid> + '_ {
190+
self.points.as_ref().expect("use with_specific_points").rows()
191+
}
192+
193+
/// Iterate through each region that has a value in this set.
194+
// We are passing query instability implications to the caller.
195+
#[rustc_lint_query_instability]
196+
#[allow(rustc::potential_query_instability)]
197+
pub(crate) fn live_regions_unordered(&self) -> impl Iterator<Item = RegionVid> + '_ {
198+
self.live_regions.as_ref().unwrap().iter().copied()
169199
}
170200

171201
/// Records `region` as being live at the given `location`.
172202
pub(crate) fn add_location(&mut self, region: RegionVid, location: Location) {
173-
debug!("LivenessValues::add_location(region={:?}, location={:?})", region, location);
174203
let point = self.elements.point_from_location(location);
175-
self.points.insert(region, point);
204+
debug!("LivenessValues::add_location(region={:?}, location={:?})", region, location);
205+
if let Some(points) = &mut self.points {
206+
points.insert(region, point);
207+
} else {
208+
if self.elements.point_in_range(point) {
209+
self.live_regions.as_mut().unwrap().insert(region);
210+
}
211+
}
176212

177213
// When available, record the loans flowing into this region as live at the given point.
178214
if let Some(loans) = self.loans.as_mut() {
@@ -185,7 +221,13 @@ impl LivenessValues {
185221
/// Records `region` as being live at all the given `points`.
186222
pub(crate) fn add_points(&mut self, region: RegionVid, points: &IntervalSet<PointIndex>) {
187223
debug!("LivenessValues::add_points(region={:?}, points={:?})", region, points);
188-
self.points.union_row(region, points);
224+
if let Some(this) = &mut self.points {
225+
this.union_row(region, points);
226+
} else {
227+
if points.iter().any(|point| self.elements.point_in_range(point)) {
228+
self.live_regions.as_mut().unwrap().insert(region);
229+
}
230+
}
189231

190232
// When available, record the loans flowing into this region as live at the given points.
191233
if let Some(loans) = self.loans.as_mut() {
@@ -201,23 +243,33 @@ impl LivenessValues {
201243

202244
/// Records `region` as being live at all the control-flow points.
203245
pub(crate) fn add_all_points(&mut self, region: RegionVid) {
204-
self.points.insert_all_into_row(region);
246+
if let Some(points) = &mut self.points {
247+
points.insert_all_into_row(region);
248+
} else {
249+
self.live_regions.as_mut().unwrap().insert(region);
250+
}
205251
}
206252

207253
/// Returns whether `region` is marked live at the given `location`.
208254
pub(crate) fn is_live_at(&self, region: RegionVid, location: Location) -> bool {
209255
let point = self.elements.point_from_location(location);
210-
self.points.row(region).is_some_and(|r| r.contains(point))
211-
}
212-
213-
/// Returns whether `region` is marked live at any location.
214-
pub(crate) fn is_live_anywhere(&self, region: RegionVid) -> bool {
215-
self.live_points(region).next().is_some()
256+
if let Some(points) = &self.points {
257+
points.row(region).is_some_and(|r| r.contains(point))
258+
} else {
259+
unreachable!(
260+
"Should be using LivenessValues::with_specific_points to ask whether live at a location"
261+
)
262+
}
216263
}
217264

218265
/// Returns an iterator of all the points where `region` is live.
219266
fn live_points(&self, region: RegionVid) -> impl Iterator<Item = PointIndex> + '_ {
220-
self.points
267+
let Some(points) = &self.points else {
268+
unreachable!(
269+
"Should be using LivenessValues::with_specific_points to ask whether live at a location"
270+
)
271+
};
272+
points
221273
.row(region)
222274
.into_iter()
223275
.flat_map(|set| set.iter())
@@ -372,7 +424,10 @@ impl<N: Idx> RegionValues<N> {
372424
/// elements for the region `from` from `values` and add them to
373425
/// the region `to` in `self`.
374426
pub(crate) fn merge_liveness(&mut self, to: N, from: RegionVid, values: &LivenessValues) {
375-
if let Some(set) = values.points.row(from) {
427+
let Some(value_points) = &values.points else {
428+
panic!("LivenessValues must track specific points for use in merge_liveness");
429+
};
430+
if let Some(set) = value_points.row(from) {
376431
self.points.union_row(to, set);
377432
}
378433
}

compiler/rustc_borrowck/src/type_check/mod.rs

+21-14
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ pub(crate) fn type_check<'mir, 'tcx>(
141141
let mut constraints = MirTypeckRegionConstraints {
142142
placeholder_indices: PlaceholderIndices::default(),
143143
placeholder_index_to_region: IndexVec::default(),
144-
liveness_constraints: LivenessValues::new(elements.clone()),
144+
liveness_constraints: LivenessValues::with_specific_points(elements.clone()),
145145
outlives_constraints: OutlivesConstraintSet::default(),
146146
member_constraints: MemberConstraintSet::default(),
147147
type_tests: Vec::default(),
@@ -544,8 +544,13 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
544544
// modify their locations.
545545
let all_facts = &mut None;
546546
let mut constraints = Default::default();
547-
let mut liveness_constraints =
548-
LivenessValues::new(Rc::new(RegionValueElements::new(promoted_body)));
547+
// We care which regions are used in the promoted, but not where in the promoted those
548+
// regions are used. So we skip tracking the specific points - that can be expensive, since
549+
// each promoted has its own LivenessValues but the regions are indexed globally within the
550+
// body. As a result we'd spend lots of time re-initializing the values.
551+
let mut liveness_constraints = LivenessValues::without_specific_points(Rc::new(
552+
RegionValueElements::new(promoted_body),
553+
));
549554
// Don't try to add borrow_region facts for the promoted MIR
550555

551556
let mut swap_constraints = |this: &mut Self| {
@@ -584,17 +589,19 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
584589
}
585590
self.cx.borrowck_context.constraints.outlives_constraints.push(constraint)
586591
}
587-
for region in liveness_constraints.regions() {
588-
// If the region is live at at least one location in the promoted MIR,
589-
// then add a liveness constraint to the main MIR for this region
590-
// at the location provided as an argument to this method
591-
if liveness_constraints.is_live_anywhere(region) {
592-
self.cx
593-
.borrowck_context
594-
.constraints
595-
.liveness_constraints
596-
.add_location(region, location);
597-
}
592+
// If the region is live at at least one location in the promoted MIR,
593+
// then add a liveness constraint to the main MIR for this region
594+
// at the location provided as an argument to this method
595+
//
596+
// add_location doesn't care about ordering so not a problem for the live regions to be
597+
// unordered.
598+
#[allow(rustc::potential_query_instability)]
599+
for region in liveness_constraints.live_regions_unordered() {
600+
self.cx
601+
.borrowck_context
602+
.constraints
603+
.liveness_constraints
604+
.add_location(region, location);
598605
}
599606
}
600607

0 commit comments

Comments
 (0)