Skip to content

Commit ff67da6

Browse files
authored
Auto merge of #36752 - jonas-schievink:vartmparg, r=eddyb
Move MIR towards a single kind of local This PR modifies MIR to handle function arguments (`Arg`), user-defined variable bindings (`Var`), compiler-generated temporaries (`Tmp`), as well as the return value pointer equally. All of them are replaced with a single `Local` type, a few functions for iterating over different kinds of locals, and a way to get the kind of local we're dealing with (mainly used in the constant qualification/propagation passes). ~~I haven't managed to fix one remaining issue: A `StorageDead` not getting emitted for a variable (see the `TODO` in the test). If that's fixed, this is basically good to go.~~ Found the issue (an off-by-one error), fix incoming. r? @eddyb for changes to constant qualification and propagation I'm not quite sure about
2 parents 704bcc0 + d2c8893 commit ff67da6

35 files changed

+645
-681
lines changed

src/librustc/mir/repr.rs

+149-141
Original file line numberDiff line numberDiff line change
@@ -70,29 +70,37 @@ pub struct Mir<'tcx> {
7070

7171
/// Rvalues promoted from this function, such as borrows of constants.
7272
/// Each of them is the Mir of a constant with the fn's type parameters
73-
/// in scope, but no vars or args and a separate set of temps.
73+
/// in scope, but a separate set of locals.
7474
pub promoted: IndexVec<Promoted, Mir<'tcx>>,
7575

7676
/// Return type of the function.
7777
pub return_ty: Ty<'tcx>,
7878

79-
/// Variables: these are stack slots corresponding to user variables. They may be
80-
/// assigned many times.
81-
pub var_decls: IndexVec<Var, VarDecl<'tcx>>,
82-
83-
/// Args: these are stack slots corresponding to the input arguments.
84-
pub arg_decls: IndexVec<Arg, ArgDecl<'tcx>>,
79+
/// Declarations of locals.
80+
///
81+
/// The first local is the return value pointer, followed by `arg_count`
82+
/// locals for the function arguments, followed by any user-declared
83+
/// variables and temporaries.
84+
pub local_decls: IndexVec<Local, LocalDecl<'tcx>>,
8585

86-
/// Temp declarations: stack slots that for temporaries created by
87-
/// the compiler. These are assigned once, but they are not SSA
88-
/// values in that it is possible to borrow them and mutate them
89-
/// through the resulting reference.
90-
pub temp_decls: IndexVec<Temp, TempDecl<'tcx>>,
86+
/// Number of arguments this function takes.
87+
///
88+
/// Starting at local 1, `arg_count` locals will be provided by the caller
89+
/// and can be assumed to be initialized.
90+
///
91+
/// If this MIR was built for a constant, this will be 0.
92+
pub arg_count: usize,
9193

9294
/// Names and capture modes of all the closure upvars, assuming
9395
/// the first argument is either the closure or a reference to it.
9496
pub upvar_decls: Vec<UpvarDecl>,
9597

98+
/// Mark an argument local (which must be a tuple) as getting passed as
99+
/// its individual components at the LLVM level.
100+
///
101+
/// This is used for the "rust-call" ABI.
102+
pub spread_arg: Option<Local>,
103+
96104
/// A span representing this MIR, for error reporting
97105
pub span: Span,
98106

@@ -108,21 +116,25 @@ impl<'tcx> Mir<'tcx> {
108116
visibility_scopes: IndexVec<VisibilityScope, VisibilityScopeData>,
109117
promoted: IndexVec<Promoted, Mir<'tcx>>,
110118
return_ty: Ty<'tcx>,
111-
var_decls: IndexVec<Var, VarDecl<'tcx>>,
112-
arg_decls: IndexVec<Arg, ArgDecl<'tcx>>,
113-
temp_decls: IndexVec<Temp, TempDecl<'tcx>>,
119+
local_decls: IndexVec<Local, LocalDecl<'tcx>>,
120+
arg_count: usize,
114121
upvar_decls: Vec<UpvarDecl>,
115122
span: Span) -> Self
116123
{
124+
// We need `arg_count` locals, and one for the return pointer
125+
assert!(local_decls.len() >= arg_count + 1,
126+
"expected at least {} locals, got {}", arg_count + 1, local_decls.len());
127+
assert_eq!(local_decls[RETURN_POINTER].ty, return_ty);
128+
117129
Mir {
118130
basic_blocks: basic_blocks,
119131
visibility_scopes: visibility_scopes,
120132
promoted: promoted,
121133
return_ty: return_ty,
122-
var_decls: var_decls,
123-
arg_decls: arg_decls,
124-
temp_decls: temp_decls,
134+
local_decls: local_decls,
135+
arg_count: arg_count,
125136
upvar_decls: upvar_decls,
137+
spread_arg: None,
126138
span: span,
127139
cache: Cache::new()
128140
}
@@ -154,56 +166,66 @@ impl<'tcx> Mir<'tcx> {
154166
dominators(self)
155167
}
156168

157-
/// Maps locals (Arg's, Var's, Temp's and ReturnPointer, in that order)
158-
/// to their index in the whole list of locals. This is useful if you
159-
/// want to treat all locals the same instead of repeating yourself.
160-
pub fn local_index(&self, lvalue: &Lvalue<'tcx>) -> Option<Local> {
161-
let idx = match *lvalue {
162-
Lvalue::Arg(arg) => arg.index(),
163-
Lvalue::Var(var) => {
164-
self.arg_decls.len() +
165-
var.index()
166-
}
167-
Lvalue::Temp(temp) => {
168-
self.arg_decls.len() +
169-
self.var_decls.len() +
170-
temp.index()
169+
#[inline]
170+
pub fn local_kind(&self, local: Local) -> LocalKind {
171+
let index = local.0 as usize;
172+
if index == 0 {
173+
debug_assert!(self.local_decls[local].mutability == Mutability::Mut,
174+
"return pointer should be mutable");
175+
176+
LocalKind::ReturnPointer
177+
} else if index < self.arg_count + 1 {
178+
LocalKind::Arg
179+
} else if self.local_decls[local].name.is_some() {
180+
LocalKind::Var
181+
} else {
182+
debug_assert!(self.local_decls[local].mutability == Mutability::Mut,
183+
"temp should be mutable");
184+
185+
LocalKind::Temp
186+
}
187+
}
188+
189+
/// Returns an iterator over all temporaries.
190+
#[inline]
191+
pub fn temps_iter<'a>(&'a self) -> impl Iterator<Item=Local> + 'a {
192+
(self.arg_count+1..self.local_decls.len()).filter_map(move |index| {
193+
let local = Local::new(index);
194+
if self.local_decls[local].source_info.is_none() {
195+
Some(local)
196+
} else {
197+
None
171198
}
172-
Lvalue::ReturnPointer => {
173-
self.arg_decls.len() +
174-
self.var_decls.len() +
175-
self.temp_decls.len()
199+
})
200+
}
201+
202+
/// Returns an iterator over all user-declared locals.
203+
#[inline]
204+
pub fn vars_iter<'a>(&'a self) -> impl Iterator<Item=Local> + 'a {
205+
(self.arg_count+1..self.local_decls.len()).filter_map(move |index| {
206+
let local = Local::new(index);
207+
if self.local_decls[local].source_info.is_none() {
208+
None
209+
} else {
210+
Some(local)
176211
}
177-
Lvalue::Static(_) |
178-
Lvalue::Projection(_) => return None
179-
};
180-
Some(Local::new(idx))
212+
})
181213
}
182214

183-
/// Counts the number of locals, such that local_index
184-
/// will always return an index smaller than this count.
185-
pub fn count_locals(&self) -> usize {
186-
self.arg_decls.len() +
187-
self.var_decls.len() +
188-
self.temp_decls.len() + 1
215+
/// Returns an iterator over all function arguments.
216+
#[inline]
217+
pub fn args_iter(&self) -> impl Iterator<Item=Local> {
218+
let arg_count = self.arg_count;
219+
(1..arg_count+1).map(Local::new)
189220
}
190221

191-
pub fn format_local(&self, local: Local) -> String {
192-
let mut index = local.index();
193-
index = match index.checked_sub(self.arg_decls.len()) {
194-
None => return format!("{:?}", Arg::new(index)),
195-
Some(index) => index,
196-
};
197-
index = match index.checked_sub(self.var_decls.len()) {
198-
None => return format!("{:?}", Var::new(index)),
199-
Some(index) => index,
200-
};
201-
index = match index.checked_sub(self.temp_decls.len()) {
202-
None => return format!("{:?}", Temp::new(index)),
203-
Some(index) => index,
204-
};
205-
debug_assert!(index == 0);
206-
return "ReturnPointer".to_string()
222+
/// Returns an iterator over all user-defined variables and compiler-generated temporaries (all
223+
/// locals that are neither arguments nor the return pointer).
224+
#[inline]
225+
pub fn vars_and_temps_iter(&self) -> impl Iterator<Item=Local> {
226+
let arg_count = self.arg_count;
227+
let local_count = self.local_decls.len();
228+
(arg_count+1..local_count).map(Local::new)
207229
}
208230

209231
/// Changes a statement to a nop. This is both faster than deleting instructions and avoids
@@ -301,53 +323,76 @@ pub enum BorrowKind {
301323
///////////////////////////////////////////////////////////////////////////
302324
// Variables and temps
303325

304-
/// A "variable" is a binding declared by the user as part of the fn
305-
/// decl, a let, etc.
306-
#[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
307-
pub struct VarDecl<'tcx> {
308-
/// `let mut x` vs `let x`
309-
pub mutability: Mutability,
310-
311-
/// name that user gave the variable; not that, internally,
312-
/// mir references variables by index
313-
pub name: Name,
326+
newtype_index!(Local, "_");
314327

315-
/// type inferred for this variable (`let x: ty = ...`)
316-
pub ty: Ty<'tcx>,
328+
pub const RETURN_POINTER: Local = Local(0);
317329

318-
/// source information (span, scope, etc.) for the declaration
319-
pub source_info: SourceInfo,
320-
}
321-
322-
/// A "temp" is a temporary that we place on the stack. They are
323-
/// anonymous, always mutable, and have only a type.
324-
#[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
325-
pub struct TempDecl<'tcx> {
326-
pub ty: Ty<'tcx>,
330+
/// Classifies locals into categories. See `Mir::local_kind`.
331+
#[derive(PartialEq, Eq, Debug)]
332+
pub enum LocalKind {
333+
/// User-declared variable binding
334+
Var,
335+
/// Compiler-introduced temporary
336+
Temp,
337+
/// Function argument
338+
Arg,
339+
/// Location of function's return value
340+
ReturnPointer,
327341
}
328342

329-
/// A "arg" is one of the function's formal arguments. These are
330-
/// anonymous and distinct from the bindings that the user declares.
331-
///
332-
/// For example, in this function:
333-
///
334-
/// ```
335-
/// fn foo((x, y): (i32, u32)) { ... }
336-
/// ```
343+
/// A MIR local.
337344
///
338-
/// there is only one argument, of type `(i32, u32)`, but two bindings
339-
/// (`x` and `y`).
345+
/// This can be a binding declared by the user, a temporary inserted by the compiler, a function
346+
/// argument, or the return pointer.
340347
#[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
341-
pub struct ArgDecl<'tcx> {
348+
pub struct LocalDecl<'tcx> {
349+
/// `let mut x` vs `let x`.
350+
///
351+
/// Temporaries and the return pointer are always mutable.
352+
pub mutability: Mutability,
353+
354+
/// Type of this local.
342355
pub ty: Ty<'tcx>,
343356

344-
/// If true, this argument is a tuple after monomorphization,
345-
/// and has to be collected from multiple actual arguments.
346-
pub spread: bool,
357+
/// Name of the local, used in debuginfo and pretty-printing.
358+
///
359+
/// Note that function arguments can also have this set to `Some(_)`
360+
/// to generate better debuginfo.
361+
pub name: Option<Name>,
347362

348-
/// Either keywords::Invalid or the name of a single-binding
349-
/// pattern associated with this argument. Useful for debuginfo.
350-
pub debug_name: Name
363+
/// For user-declared variables, stores their source information.
364+
///
365+
/// For temporaries, this is `None`.
366+
///
367+
/// This is the primary way to differentiate between user-declared
368+
/// variables and compiler-generated temporaries.
369+
pub source_info: Option<SourceInfo>,
370+
}
371+
372+
impl<'tcx> LocalDecl<'tcx> {
373+
/// Create a new `LocalDecl` for a temporary.
374+
#[inline]
375+
pub fn new_temp(ty: Ty<'tcx>) -> Self {
376+
LocalDecl {
377+
mutability: Mutability::Mut,
378+
ty: ty,
379+
name: None,
380+
source_info: None,
381+
}
382+
}
383+
384+
/// Builds a `LocalDecl` for the return pointer.
385+
///
386+
/// This must be inserted into the `local_decls` list as the first local.
387+
#[inline]
388+
pub fn new_return_pointer(return_ty: Ty) -> LocalDecl {
389+
LocalDecl {
390+
mutability: Mutability::Mut,
391+
ty: return_ty,
392+
source_info: None,
393+
name: None, // FIXME maybe we do want some name here?
394+
}
395+
}
351396
}
352397

353398
/// A closure capture, with its name and mode.
@@ -439,7 +484,7 @@ pub enum TerminatorKind<'tcx> {
439484
/// continue. Emitted by build::scope::diverge_cleanup.
440485
Resume,
441486

442-
/// Indicates a normal return. The ReturnPointer lvalue should
487+
/// Indicates a normal return. The return pointer lvalue should
443488
/// have been filled in by now. This should occur at most once.
444489
Return,
445490

@@ -756,31 +801,16 @@ impl<'tcx> Debug for Statement<'tcx> {
756801
///////////////////////////////////////////////////////////////////////////
757802
// Lvalues
758803

759-
newtype_index!(Var, "var");
760-
newtype_index!(Temp, "tmp");
761-
newtype_index!(Arg, "arg");
762-
newtype_index!(Local, "local");
763-
764804
/// A path to a value; something that can be evaluated without
765805
/// changing or disturbing program state.
766806
#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable)]
767807
pub enum Lvalue<'tcx> {
768-
/// local variable declared by the user
769-
Var(Var),
770-
771-
/// temporary introduced during lowering into MIR
772-
Temp(Temp),
773-
774-
/// formal parameter of the function; note that these are NOT the
775-
/// bindings that the user declares, which are vars
776-
Arg(Arg),
808+
/// local variable
809+
Local(Local),
777810

778811
/// static or static mut variable
779812
Static(DefId),
780813

781-
/// the return pointer of the fn
782-
ReturnPointer,
783-
784814
/// projection out of an lvalue (access a field, deref a pointer, etc)
785815
Projection(Box<LvalueProjection<'tcx>>),
786816
}
@@ -862,38 +892,16 @@ impl<'tcx> Lvalue<'tcx> {
862892
elem: elem,
863893
}))
864894
}
865-
866-
pub fn from_local(mir: &Mir<'tcx>, local: Local) -> Lvalue<'tcx> {
867-
let mut index = local.index();
868-
index = match index.checked_sub(mir.arg_decls.len()) {
869-
None => return Lvalue::Arg(Arg(index as u32)),
870-
Some(index) => index,
871-
};
872-
index = match index.checked_sub(mir.var_decls.len()) {
873-
None => return Lvalue::Var(Var(index as u32)),
874-
Some(index) => index,
875-
};
876-
index = match index.checked_sub(mir.temp_decls.len()) {
877-
None => return Lvalue::Temp(Temp(index as u32)),
878-
Some(index) => index,
879-
};
880-
debug_assert!(index == 0);
881-
Lvalue::ReturnPointer
882-
}
883895
}
884896

885897
impl<'tcx> Debug for Lvalue<'tcx> {
886898
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
887899
use self::Lvalue::*;
888900

889901
match *self {
890-
Var(id) => write!(fmt, "{:?}", id),
891-
Arg(id) => write!(fmt, "{:?}", id),
892-
Temp(id) => write!(fmt, "{:?}", id),
902+
Local(id) => write!(fmt, "{:?}", id),
893903
Static(def_id) =>
894904
write!(fmt, "{}", ty::tls::with(|tcx| tcx.item_path_str(def_id))),
895-
ReturnPointer =>
896-
write!(fmt, "return"),
897905
Projection(ref data) =>
898906
match data.elem {
899907
ProjectionElem::Downcast(ref adt_def, index) =>

0 commit comments

Comments
 (0)