Skip to content

Commit 0ea5dc5

Browse files
committed
Don't alloca for unused locals
1 parent 5ba6db1 commit 0ea5dc5

File tree

7 files changed

+156
-26
lines changed

7 files changed

+156
-26
lines changed

Diff for: compiler/rustc_codegen_ssa/src/mir/analyze.rs

+9-3
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,18 @@ use crate::traits::*;
1515

1616
pub(crate) fn non_ssa_locals<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
1717
fx: &FunctionCx<'a, 'tcx, Bx>,
18+
traversal_order: &[mir::BasicBlock],
19+
reachable_locals: &BitSet<mir::Local>,
1820
) -> BitSet<mir::Local> {
1921
let mir = fx.mir;
2022
let dominators = mir.basic_blocks.dominators();
2123
let locals = mir
2224
.local_decls
23-
.iter()
24-
.map(|decl| {
25+
.iter_enumerated()
26+
.map(|(local, decl)| {
27+
if !reachable_locals.contains(local) {
28+
return LocalKind::Unused;
29+
}
2530
let ty = fx.monomorphize(decl.ty);
2631
let layout = fx.cx.spanned_layout_of(ty, decl.source_info.span);
2732
if layout.is_zst() {
@@ -44,7 +49,8 @@ pub(crate) fn non_ssa_locals<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
4449
// If there exists a local definition that dominates all uses of that local,
4550
// the definition should be visited first. Traverse blocks in an order that
4651
// is a topological sort of dominance partial order.
47-
for (bb, data) in traversal::reverse_postorder(mir) {
52+
for bb in traversal_order.iter().copied() {
53+
let data = &mir.basic_blocks[bb];
4854
analyzer.visit_basic_block_data(bb, data);
4955
}
5056

Diff for: compiler/rustc_codegen_ssa/src/mir/mod.rs

+12-12
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,9 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
192192
})
193193
.collect();
194194

195+
let (traversal_order, reachable_locals) =
196+
traversal::mono_reachable_reverse_postorder(mir, cx.tcx(), instance);
197+
195198
let mut fx = FunctionCx {
196199
instance,
197200
mir,
@@ -218,7 +221,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
218221

219222
fx.per_local_var_debug_info = fx.compute_per_local_var_debug_info(&mut start_bx);
220223

221-
let memory_locals = analyze::non_ssa_locals(&fx);
224+
let memory_locals = analyze::non_ssa_locals(&fx, &traversal_order, &reachable_locals);
222225

223226
// Allocate variable and temp allocas
224227
let local_values = {
@@ -277,17 +280,14 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
277280
// So drop the builder of `start_llbb` to avoid having two at the same time.
278281
drop(start_bx);
279282

280-
let reachable_blocks = traversal::mono_reachable_as_bitset(mir, cx.tcx(), instance);
281-
282-
// Codegen the body of each block using reverse postorder
283-
for (bb, _) in traversal::reverse_postorder(mir) {
284-
if reachable_blocks.contains(bb) {
285-
fx.codegen_block(bb);
286-
} else {
287-
// We want to skip this block, because it's not reachable. But we still create
288-
// the block so terminators in other blocks can reference it.
289-
fx.codegen_block_as_unreachable(bb);
290-
}
283+
let mut unreached_blocks = BitSet::new_filled(mir.basic_blocks.len());
284+
// Codegen the body of each reachable block using our reverse postorder list.
285+
for bb in traversal_order {
286+
fx.codegen_block(bb);
287+
unreached_blocks.remove(bb);
288+
}
289+
for bb in unreached_blocks.iter() {
290+
fx.codegen_block_as_unreachable(bb);
291291
}
292292
}
293293

Diff for: compiler/rustc_middle/src/mir/mod.rs

+13
Original file line numberDiff line numberDiff line change
@@ -1424,6 +1424,19 @@ impl<'tcx> BasicBlockData<'tcx> {
14241424
pub fn is_empty_unreachable(&self) -> bool {
14251425
self.statements.is_empty() && matches!(self.terminator().kind, TerminatorKind::Unreachable)
14261426
}
1427+
1428+
/// Like [`Terminator::successors`] but tries to use information available from the [`Instance`]
1429+
/// to skip successors like the `false` side of an `if const {`.
1430+
///
1431+
/// This is used to implement [`traversal::mono_reachable`] and
1432+
/// [`traversal::mono_reachable_reverse_postorder`].
1433+
pub fn mono_successors(&self, tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> Successors<'_> {
1434+
if let Some((bits, targets)) = Body::try_const_mono_switchint(tcx, instance, self) {
1435+
targets.successors_for_value(bits)
1436+
} else {
1437+
self.terminator().successors()
1438+
}
1439+
}
14271440
}
14281441

14291442
///////////////////////////////////////////////////////////////////////////

Diff for: compiler/rustc_middle/src/mir/terminator.rs

+11
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,17 @@ mod helper {
413413
use super::*;
414414
pub type Successors<'a> = impl DoubleEndedIterator<Item = BasicBlock> + 'a;
415415
pub type SuccessorsMut<'a> = impl DoubleEndedIterator<Item = &'a mut BasicBlock> + 'a;
416+
417+
impl SwitchTargets {
418+
/// Like [`SwitchTargets::target_for_value`], but returning the same type as
419+
/// [`Terminator::successors`].
420+
#[inline]
421+
pub fn successors_for_value(&self, value: u128) -> Successors<'_> {
422+
let target = self.target_for_value(value);
423+
(&[]).into_iter().copied().chain(Some(target))
424+
}
425+
}
426+
416427
impl<'tcx> TerminatorKind<'tcx> {
417428
#[inline]
418429
pub fn successors(&self) -> Successors<'_> {

Diff for: compiler/rustc_middle/src/mir/traversal.rs

+87-8
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use super::*;
2+
use crate::mir::visit::Visitor;
23

34
/// Preorder traversal of a graph.
45
///
@@ -232,6 +233,90 @@ pub fn postorder<'a, 'tcx>(
232233
reverse_postorder(body).rev()
233234
}
234235

236+
struct UsedLocals(BitSet<Local>);
237+
238+
impl<'tcx> Visitor<'tcx> for UsedLocals {
239+
fn visit_local(
240+
&mut self,
241+
local: Local,
242+
_ctx: crate::mir::visit::PlaceContext,
243+
_location: Location,
244+
) {
245+
self.0.insert(local);
246+
}
247+
}
248+
249+
struct MonoReachablePostorder<'a, 'tcx> {
250+
basic_blocks: &'a IndexSlice<BasicBlock, BasicBlockData<'tcx>>,
251+
visited: BitSet<BasicBlock>,
252+
visit_stack: Vec<(BasicBlock, Successors<'a>)>,
253+
locals: UsedLocals,
254+
tcx: TyCtxt<'tcx>,
255+
instance: Instance<'tcx>,
256+
}
257+
258+
impl<'a, 'tcx> MonoReachablePostorder<'a, 'tcx> {
259+
fn new(
260+
body: &'a Body<'tcx>,
261+
tcx: TyCtxt<'tcx>,
262+
instance: Instance<'tcx>,
263+
) -> MonoReachablePostorder<'a, 'tcx> {
264+
let basic_blocks = &body.basic_blocks;
265+
let mut po = MonoReachablePostorder {
266+
basic_blocks,
267+
visited: BitSet::new_empty(basic_blocks.len()),
268+
visit_stack: Vec::new(),
269+
locals: UsedLocals(BitSet::new_empty(body.local_decls.len())),
270+
tcx,
271+
instance,
272+
};
273+
274+
po.visit(START_BLOCK);
275+
po.traverse_successor();
276+
po
277+
}
278+
279+
fn visit(&mut self, bb: BasicBlock) {
280+
if !self.visited.insert(bb) {
281+
return;
282+
}
283+
let data = &self.basic_blocks[bb];
284+
self.locals.visit_basic_block_data(bb, data);
285+
let successors = data.mono_successors(self.tcx, self.instance);
286+
self.visit_stack.push((bb, successors));
287+
}
288+
289+
fn traverse_successor(&mut self) {
290+
while let Some(bb) = self.visit_stack.last_mut().and_then(|(_, iter)| iter.next_back()) {
291+
self.visit(bb);
292+
}
293+
}
294+
}
295+
296+
impl<'tcx> Iterator for MonoReachablePostorder<'_, 'tcx> {
297+
type Item = BasicBlock;
298+
299+
fn next(&mut self) -> Option<BasicBlock> {
300+
let (bb, _) = self.visit_stack.pop()?;
301+
self.traverse_successor();
302+
Some(bb)
303+
}
304+
}
305+
306+
pub fn mono_reachable_reverse_postorder<'a, 'tcx>(
307+
body: &'a Body<'tcx>,
308+
tcx: TyCtxt<'tcx>,
309+
instance: Instance<'tcx>,
310+
) -> (Vec<BasicBlock>, BitSet<Local>) {
311+
let mut iter = MonoReachablePostorder::new(body, tcx, instance);
312+
let mut items = Vec::with_capacity(body.basic_blocks.len());
313+
while let Some(block) = iter.next() {
314+
items.push(block);
315+
}
316+
items.reverse();
317+
(items, iter.locals.0)
318+
}
319+
235320
/// Returns an iterator over all basic blocks reachable from the `START_BLOCK` in no particular
236321
/// order.
237322
///
@@ -358,14 +443,8 @@ impl<'a, 'tcx> Iterator for MonoReachable<'a, 'tcx> {
358443

359444
let data = &self.body[idx];
360445

361-
if let Some((bits, targets)) =
362-
Body::try_const_mono_switchint(self.tcx, self.instance, data)
363-
{
364-
let target = targets.target_for_value(bits);
365-
self.add_work([target]);
366-
} else {
367-
self.add_work(data.terminator().successors());
368-
}
446+
let targets = data.mono_successors(self.tcx, self.instance);
447+
self.add_work(targets);
369448

370449
return Some((idx, data));
371450
}

Diff for: tests/codegen/constant-branch.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,8 @@ pub fn if_constant_match() {
4141
_ => 4,
4242
};
4343

44-
// CHECK: br label %[[MINUS1:.+]]
44+
// CHECK: br label %{{.+}}
4545
_ = match -1 {
46-
// CHECK: [[MINUS1]]:
47-
// CHECK: store i32 1
4846
-1 => 1,
4947
_ => 0,
5048
}

Diff for: tests/codegen/no-alloca-inside-if-false.rs

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
//@ compile-flags: -Cno-prepopulate-passes -Copt-level=0
2+
3+
#![crate_type = "lib"]
4+
5+
#[inline(never)]
6+
fn test<const SIZE: usize>() {
7+
// CHECK-LABEL: no_alloca_inside_if_false::test
8+
// CHECK: start:
9+
// CHECK-NEXT: %0 = alloca
10+
// CHECK-NEXT: %vec = alloca
11+
// CHECK-NOT: %arr = alloca
12+
if const { SIZE < 4096 } {
13+
let arr = [0u8; SIZE];
14+
std::hint::black_box(&arr);
15+
} else {
16+
let vec = vec![0u8; SIZE];
17+
std::hint::black_box(&vec);
18+
}
19+
}
20+
21+
pub fn main() {
22+
test::<8192>();
23+
}

0 commit comments

Comments
 (0)