Skip to content

Commit 3824017

Browse files
committed
Auto merge of #86166 - tmiasko:no-alloca-for-zsts, r=nagisa
Do not emit alloca for ZST locals with multiple assignments This extends 35566bf to additionally stop emitting unnecessary allocas for zero sized locals that are assigned multiple times. When rebuilding the standard library with `-Zbuild-std` this reduces the number of locals that require an allocation from 62315 to 61767.
2 parents 6a540bd + 40c9aae commit 3824017

File tree

2 files changed

+76
-95
lines changed

2 files changed

+76
-95
lines changed

compiler/rustc_codegen_ssa/src/mir/analyze.rs

+74-93
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use super::FunctionCx;
55
use crate::traits::*;
66
use rustc_data_structures::graph::dominators::Dominators;
77
use rustc_index::bit_set::BitSet;
8-
use rustc_index::vec::{Idx, IndexVec};
8+
use rustc_index::vec::IndexVec;
99
use rustc_middle::mir::traversal;
1010
use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
1111
use rustc_middle::mir::{self, Location, TerminatorKind};
@@ -16,7 +16,29 @@ pub fn non_ssa_locals<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
1616
fx: &FunctionCx<'a, 'tcx, Bx>,
1717
) -> BitSet<mir::Local> {
1818
let mir = fx.mir;
19-
let mut analyzer = LocalAnalyzer::new(fx);
19+
let dominators = mir.dominators();
20+
let locals = mir
21+
.local_decls
22+
.iter()
23+
.map(|decl| {
24+
let ty = fx.monomorphize(decl.ty);
25+
let layout = fx.cx.spanned_layout_of(ty, decl.source_info.span);
26+
if layout.is_zst() {
27+
LocalKind::ZST
28+
} else if fx.cx.is_backend_immediate(layout) || fx.cx.is_backend_scalar_pair(layout) {
29+
LocalKind::Unused
30+
} else {
31+
LocalKind::Memory
32+
}
33+
})
34+
.collect();
35+
36+
let mut analyzer = LocalAnalyzer { fx, dominators, locals };
37+
38+
// Arguments get assigned to by means of the function being called
39+
for arg in mir.args_iter() {
40+
analyzer.assign(arg, mir::START_BLOCK.start_location());
41+
}
2042

2143
// If there exists a local definition that dominates all uses of that local,
2244
// the definition should be visited first. Traverse blocks in preorder which
@@ -25,76 +47,45 @@ pub fn non_ssa_locals<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
2547
analyzer.visit_basic_block_data(bb, data);
2648
}
2749

28-
for (local, decl) in mir.local_decls.iter_enumerated() {
29-
let ty = fx.monomorphize(decl.ty);
30-
debug!("local {:?} has type `{}`", local, ty);
31-
let layout = fx.cx.spanned_layout_of(ty, decl.source_info.span);
32-
if fx.cx.is_backend_immediate(layout) {
33-
// These sorts of types are immediates that we can store
34-
// in an Value without an alloca.
35-
} else if fx.cx.is_backend_scalar_pair(layout) {
36-
// We allow pairs and uses of any of their 2 fields.
37-
} else {
38-
// These sorts of types require an alloca. Note that
39-
// is_llvm_immediate() may *still* be true, particularly
40-
// for newtypes, but we currently force some types
41-
// (e.g., structs) into an alloca unconditionally, just so
42-
// that we don't have to deal with having two pathways
43-
// (gep vs extractvalue etc).
44-
analyzer.not_ssa(local);
50+
let mut non_ssa_locals = BitSet::new_empty(analyzer.locals.len());
51+
for (local, kind) in analyzer.locals.iter_enumerated() {
52+
if matches!(kind, LocalKind::Memory) {
53+
non_ssa_locals.insert(local);
4554
}
4655
}
4756

48-
analyzer.non_ssa_locals
57+
non_ssa_locals
58+
}
59+
60+
#[derive(Copy, Clone, PartialEq, Eq)]
61+
enum LocalKind {
62+
ZST,
63+
/// A local that requires an alloca.
64+
Memory,
65+
/// A scalar or a scalar pair local that is neither defined nor used.
66+
Unused,
67+
/// A scalar or a scalar pair local with a single definition that dominates all uses.
68+
SSA(mir::Location),
4969
}
5070

5171
struct LocalAnalyzer<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> {
5272
fx: &'mir FunctionCx<'a, 'tcx, Bx>,
5373
dominators: Dominators<mir::BasicBlock>,
54-
non_ssa_locals: BitSet<mir::Local>,
55-
// The location of the first visited direct assignment to each
56-
// local, or an invalid location (out of bounds `block` index).
57-
first_assignment: IndexVec<mir::Local, Location>,
74+
locals: IndexVec<mir::Local, LocalKind>,
5875
}
5976

6077
impl<Bx: BuilderMethods<'a, 'tcx>> LocalAnalyzer<'mir, 'a, 'tcx, Bx> {
61-
fn new(fx: &'mir FunctionCx<'a, 'tcx, Bx>) -> Self {
62-
let invalid_location = mir::BasicBlock::new(fx.mir.basic_blocks().len()).start_location();
63-
let dominators = fx.mir.dominators();
64-
let mut analyzer = LocalAnalyzer {
65-
fx,
66-
dominators,
67-
non_ssa_locals: BitSet::new_empty(fx.mir.local_decls.len()),
68-
first_assignment: IndexVec::from_elem(invalid_location, &fx.mir.local_decls),
69-
};
70-
71-
// Arguments get assigned to by means of the function being called
72-
for arg in fx.mir.args_iter() {
73-
analyzer.first_assignment[arg] = mir::START_BLOCK.start_location();
74-
}
75-
76-
analyzer
77-
}
78-
79-
fn first_assignment(&self, local: mir::Local) -> Option<Location> {
80-
let location = self.first_assignment[local];
81-
if location.block.index() < self.fx.mir.basic_blocks().len() {
82-
Some(location)
83-
} else {
84-
None
85-
}
86-
}
87-
88-
fn not_ssa(&mut self, local: mir::Local) {
89-
debug!("marking {:?} as non-SSA", local);
90-
self.non_ssa_locals.insert(local);
91-
}
92-
9378
fn assign(&mut self, local: mir::Local, location: Location) {
94-
if self.first_assignment(local).is_some() {
95-
self.not_ssa(local);
96-
} else {
97-
self.first_assignment[local] = location;
79+
let kind = &mut self.locals[local];
80+
match *kind {
81+
LocalKind::ZST => {}
82+
LocalKind::Memory => {}
83+
LocalKind::Unused => {
84+
*kind = LocalKind::SSA(location);
85+
}
86+
LocalKind::SSA(_) => {
87+
*kind = LocalKind::Memory;
88+
}
9889
}
9990
}
10091

@@ -175,11 +166,13 @@ impl<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> Visitor<'tcx>
175166
) {
176167
debug!("visit_assign(place={:?}, rvalue={:?})", place, rvalue);
177168

178-
if let Some(index) = place.as_local() {
179-
self.assign(index, location);
180-
let decl_span = self.fx.mir.local_decls[index].source_info.span;
181-
if !self.fx.rvalue_creates_operand(rvalue, decl_span) {
182-
self.not_ssa(index);
169+
if let Some(local) = place.as_local() {
170+
self.assign(local, location);
171+
if self.locals[local] != LocalKind::Memory {
172+
let decl_span = self.fx.mir.local_decls[local].source_info.span;
173+
if !self.fx.rvalue_creates_operand(rvalue, decl_span) {
174+
self.locals[local] = LocalKind::Memory;
175+
}
183176
}
184177
} else {
185178
self.visit_place(place, PlaceContext::MutatingUse(MutatingUseContext::Store), location);
@@ -204,32 +197,18 @@ impl<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> Visitor<'tcx>
204197

205198
PlaceContext::NonMutatingUse(
206199
NonMutatingUseContext::Copy | NonMutatingUseContext::Move,
207-
) => {
200+
) => match &mut self.locals[local] {
201+
LocalKind::ZST => {}
202+
LocalKind::Memory => {}
203+
LocalKind::SSA(def) if def.dominates(location, &self.dominators) => {}
208204
// Reads from uninitialized variables (e.g., in dead code, after
209205
// optimizations) require locals to be in (uninitialized) memory.
210206
// N.B., there can be uninitialized reads of a local visited after
211207
// an assignment to that local, if they happen on disjoint paths.
212-
let ssa_read = match self.first_assignment(local) {
213-
Some(assignment_location) => {
214-
assignment_location.dominates(location, &self.dominators)
215-
}
216-
None => {
217-
debug!("No first assignment found for {:?}", local);
218-
// We have not seen any assignment to the local yet,
219-
// but before marking not_ssa, check if it is a ZST,
220-
// in which case we don't need to initialize the local.
221-
let ty = self.fx.mir.local_decls[local].ty;
222-
let ty = self.fx.monomorphize(ty);
223-
224-
let is_zst = self.fx.cx.layout_of(ty).is_zst();
225-
debug!("is_zst: {}", is_zst);
226-
is_zst
227-
}
228-
};
229-
if !ssa_read {
230-
self.not_ssa(local);
208+
kind @ (LocalKind::Unused | LocalKind::SSA(_)) => {
209+
*kind = LocalKind::Memory;
231210
}
232-
}
211+
},
233212

234213
PlaceContext::MutatingUse(
235214
MutatingUseContext::Store
@@ -246,16 +225,18 @@ impl<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> Visitor<'tcx>
246225
| NonMutatingUseContext::AddressOf
247226
| NonMutatingUseContext::Projection,
248227
) => {
249-
self.not_ssa(local);
228+
self.locals[local] = LocalKind::Memory;
250229
}
251230

252231
PlaceContext::MutatingUse(MutatingUseContext::Drop) => {
253-
let ty = self.fx.mir.local_decls[local].ty;
254-
let ty = self.fx.monomorphize(ty);
255-
256-
// Only need the place if we're actually dropping it.
257-
if self.fx.cx.type_needs_drop(ty) {
258-
self.not_ssa(local);
232+
let kind = &mut self.locals[local];
233+
if *kind != LocalKind::Memory {
234+
let ty = self.fx.mir.local_decls[local].ty;
235+
let ty = self.fx.monomorphize(ty);
236+
if self.fx.cx.type_needs_drop(ty) {
237+
// Only need the place if we're actually dropping it.
238+
*kind = LocalKind::Memory;
239+
}
259240
}
260241
}
261242
}
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
error: values of the type `[u8; 18446744073709551615]` are too big for the current architecture
2-
--> $DIR/issue-69485-var-size-diffs-too-large.rs:6:12
2+
--> $DIR/issue-69485-var-size-diffs-too-large.rs:6:5
33
|
44
LL | Bug::V([0; !0]);
5-
| ^^^^^^^
5+
| ^^^^^^^^^^^^^^^
66

77
error: aborting due to previous error
88

0 commit comments

Comments
 (0)