Skip to content

Commit 45cde97

Browse files
authored
Auto merge of #34189 - eddyb:mir-trans-imm, r=nagisa
trans: generalize immediate temporaries to all MIR locals. Added `Mir::local_index` which gives you an unified index for `Arg`, `Var`, `Temp` and `ReturnPointer`. Also available is `Mir::count_locals` which returns the total number of the above locals. This simplifies a lot of the code which can treat all of the local lvalues in the same manner. If we had `-> impl Iterator`, I could have added a bunch of useful `Ty` or `Lvalue` iterators for all locals. We could of course manually write such iterators as they are needed. The only place which currently takes advantage of unified locals is trans' alloca elision. Currently it's not as good as it could be, due to our usage of `llvm.dbg.declare` in debug mode. But passing some arguments and variables as immediates has some effect on release-mode `libsyntax`: Old trans: ``` time: 11.500; rss: 710MB translation time: 0.002; rss: 710MB assert dep graph time: 0.000; rss: 710MB serialize dep graph time: 4.410; rss: 628MB llvm function passes [0] time: 84.485; rss: 633MB llvm module passes [0] time: 23.898; rss: 634MB codegen passes [0] time: 0.002; rss: 634MB codegen passes [0] time: 113.408; rss: 634MB LLVM passes ``` `-Z orbit`, previously: ``` time: 12.588; rss: 723MB translation time: 0.002; rss: 723MB assert dep graph time: 0.000; rss: 723MB serialize dep graph time: 4.597; rss: 642MB llvm function passes [0] time: 77.347; rss: 646MB llvm module passes [0] time: 24.703; rss: 648MB codegen passes [0] time: 0.002; rss: 615MB codegen passes [0] time: 107.233; rss: 615MB LLVM passes ``` `-Z orbit`, after this PR: ``` time: 13.820; rss: 672MB translation time: 0.002; rss: 672MB assert dep graph time: 0.000; rss: 672MB serialize dep graph time: 3.969; rss: 591MB llvm function passes [0] time: 72.294; rss: 595MB llvm module passes [0] time: 24.610; rss: 597MB codegen passes [0] time: 0.002; rss: 597MB codegen passes [0] time: 101.439; rss: 597MB LLVM passes ```
2 parents 4ba60ab + 7279af8 commit 45cde97

File tree

12 files changed

+497
-340
lines changed

12 files changed

+497
-340
lines changed

src/librustc/mir/repr.rs

+35
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,40 @@ impl<'tcx> Mir<'tcx> {
144144
pub fn predecessors_for(&self, bb: BasicBlock) -> Ref<Vec<BasicBlock>> {
145145
Ref::map(self.predecessors(), |p| &p[bb])
146146
}
147+
148+
/// Maps locals (Arg's, Var's, Temp's and ReturnPointer, in that order)
149+
/// to their index in the whole list of locals. This is useful if you
150+
/// want to treat all locals the same instead of repeating yourself.
151+
pub fn local_index(&self, lvalue: &Lvalue<'tcx>) -> Option<Local> {
152+
let idx = match *lvalue {
153+
Lvalue::Arg(arg) => arg.index(),
154+
Lvalue::Var(var) => {
155+
self.arg_decls.len() +
156+
var.index()
157+
}
158+
Lvalue::Temp(temp) => {
159+
self.arg_decls.len() +
160+
self.var_decls.len() +
161+
temp.index()
162+
}
163+
Lvalue::ReturnPointer => {
164+
self.arg_decls.len() +
165+
self.var_decls.len() +
166+
self.temp_decls.len()
167+
}
168+
Lvalue::Static(_) |
169+
Lvalue::Projection(_) => return None
170+
};
171+
Some(Local::new(idx))
172+
}
173+
174+
/// Counts the number of locals, such that that local_index
175+
/// will always return an index smaller than this count.
176+
pub fn count_locals(&self) -> usize {
177+
self.arg_decls.len() +
178+
self.var_decls.len() +
179+
self.temp_decls.len() + 1
180+
}
147181
}
148182

149183
impl<'tcx> Index<BasicBlock> for Mir<'tcx> {
@@ -663,6 +697,7 @@ impl<'tcx> Debug for Statement<'tcx> {
663697
newtype_index!(Var, "var");
664698
newtype_index!(Temp, "tmp");
665699
newtype_index!(Arg, "arg");
700+
newtype_index!(Local, "local");
666701

667702
/// A path to a value; something that can be evaluated without
668703
/// changing or disturbing program state.

src/librustc/ty/sty.rs

+7
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,13 @@ impl<'tcx> FnOutput<'tcx> {
492492
ty::FnDiverging => def
493493
}
494494
}
495+
496+
pub fn maybe_converging(self) -> Option<Ty<'tcx>> {
497+
match self {
498+
ty::FnConverging(t) => Some(t),
499+
ty::FnDiverging => None
500+
}
501+
}
495502
}
496503

497504
pub type PolyFnOutput<'tcx> = Binder<FnOutput<'tcx>>;

src/librustc_trans/mir/analyze.rs

+89-52
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
//! An analysis to determine which temporaries require allocas and
11+
//! An analysis to determine which locals require allocas and
1212
//! which do not.
1313
1414
use rustc_data_structures::bitvec::BitVector;
@@ -18,18 +18,23 @@ use rustc::mir::repr::TerminatorKind;
1818
use rustc::mir::visit::{Visitor, LvalueContext};
1919
use rustc::mir::traversal;
2020
use common::{self, Block, BlockAndBuilder};
21+
use glue;
2122
use super::rvalue;
2223

23-
pub fn lvalue_temps<'bcx,'tcx>(bcx: Block<'bcx,'tcx>,
24-
mir: &mir::Mir<'tcx>) -> BitVector {
24+
pub fn lvalue_locals<'bcx, 'tcx>(bcx: Block<'bcx,'tcx>,
25+
mir: &mir::Mir<'tcx>) -> BitVector {
2526
let bcx = bcx.build();
26-
let mut analyzer = TempAnalyzer::new(mir, &bcx, mir.temp_decls.len());
27+
let mut analyzer = LocalAnalyzer::new(mir, &bcx);
2728

2829
analyzer.visit_mir(mir);
2930

30-
for (index, temp_decl) in mir.temp_decls.iter().enumerate() {
31-
let ty = bcx.monomorphize(&temp_decl.ty);
32-
debug!("temp {:?} has type {:?}", index, ty);
31+
let local_types = mir.arg_decls.iter().map(|a| a.ty)
32+
.chain(mir.var_decls.iter().map(|v| v.ty))
33+
.chain(mir.temp_decls.iter().map(|t| t.ty))
34+
.chain(mir.return_ty.maybe_converging());
35+
for (index, ty) in local_types.enumerate() {
36+
let ty = bcx.monomorphize(&ty);
37+
debug!("local {} has type {:?}", index, ty);
3338
if ty.is_scalar() ||
3439
ty.is_unique() ||
3540
ty.is_region_ptr() ||
@@ -49,76 +54,97 @@ pub fn lvalue_temps<'bcx,'tcx>(bcx: Block<'bcx,'tcx>,
4954
// (e.g. structs) into an alloca unconditionally, just so
5055
// that we don't have to deal with having two pathways
5156
// (gep vs extractvalue etc).
52-
analyzer.mark_as_lvalue(index);
57+
analyzer.mark_as_lvalue(mir::Local::new(index));
5358
}
5459
}
5560

56-
analyzer.lvalue_temps
61+
analyzer.lvalue_locals
5762
}
5863

59-
struct TempAnalyzer<'mir, 'bcx: 'mir, 'tcx: 'bcx> {
64+
struct LocalAnalyzer<'mir, 'bcx: 'mir, 'tcx: 'bcx> {
6065
mir: &'mir mir::Mir<'tcx>,
6166
bcx: &'mir BlockAndBuilder<'bcx, 'tcx>,
62-
lvalue_temps: BitVector,
67+
lvalue_locals: BitVector,
6368
seen_assigned: BitVector
6469
}
6570

66-
impl<'mir, 'bcx, 'tcx> TempAnalyzer<'mir, 'bcx, 'tcx> {
71+
impl<'mir, 'bcx, 'tcx> LocalAnalyzer<'mir, 'bcx, 'tcx> {
6772
fn new(mir: &'mir mir::Mir<'tcx>,
68-
bcx: &'mir BlockAndBuilder<'bcx, 'tcx>,
69-
temp_count: usize) -> TempAnalyzer<'mir, 'bcx, 'tcx> {
70-
TempAnalyzer {
73+
bcx: &'mir BlockAndBuilder<'bcx, 'tcx>)
74+
-> LocalAnalyzer<'mir, 'bcx, 'tcx> {
75+
let local_count = mir.count_locals();
76+
LocalAnalyzer {
7177
mir: mir,
7278
bcx: bcx,
73-
lvalue_temps: BitVector::new(temp_count),
74-
seen_assigned: BitVector::new(temp_count)
79+
lvalue_locals: BitVector::new(local_count),
80+
seen_assigned: BitVector::new(local_count)
7581
}
7682
}
7783

78-
fn mark_as_lvalue(&mut self, temp: usize) {
79-
debug!("marking temp {} as lvalue", temp);
80-
self.lvalue_temps.insert(temp);
84+
fn mark_as_lvalue(&mut self, local: mir::Local) {
85+
debug!("marking {:?} as lvalue", local);
86+
self.lvalue_locals.insert(local.index());
8187
}
8288

83-
fn mark_assigned(&mut self, temp: usize) {
84-
if !self.seen_assigned.insert(temp) {
85-
self.mark_as_lvalue(temp);
89+
fn mark_assigned(&mut self, local: mir::Local) {
90+
if !self.seen_assigned.insert(local.index()) {
91+
self.mark_as_lvalue(local);
8692
}
8793
}
8894
}
8995

90-
impl<'mir, 'bcx, 'tcx> Visitor<'tcx> for TempAnalyzer<'mir, 'bcx, 'tcx> {
96+
impl<'mir, 'bcx, 'tcx> Visitor<'tcx> for LocalAnalyzer<'mir, 'bcx, 'tcx> {
9197
fn visit_assign(&mut self,
9298
block: mir::BasicBlock,
9399
lvalue: &mir::Lvalue<'tcx>,
94100
rvalue: &mir::Rvalue<'tcx>) {
95101
debug!("visit_assign(block={:?}, lvalue={:?}, rvalue={:?})", block, lvalue, rvalue);
96102

97-
match *lvalue {
98-
mir::Lvalue::Temp(temp) => {
99-
self.mark_assigned(temp.index());
100-
if !rvalue::rvalue_creates_operand(self.mir, self.bcx, rvalue) {
101-
self.mark_as_lvalue(temp.index());
102-
}
103-
}
104-
_ => {
105-
self.visit_lvalue(lvalue, LvalueContext::Store);
103+
if let Some(index) = self.mir.local_index(lvalue) {
104+
self.mark_assigned(index);
105+
if !rvalue::rvalue_creates_operand(self.mir, self.bcx, rvalue) {
106+
self.mark_as_lvalue(index);
106107
}
108+
} else {
109+
self.visit_lvalue(lvalue, LvalueContext::Store);
107110
}
108111

109112
self.visit_rvalue(rvalue);
110113
}
111114

115+
fn visit_terminator_kind(&mut self,
116+
block: mir::BasicBlock,
117+
kind: &mir::TerminatorKind<'tcx>) {
118+
match *kind {
119+
mir::TerminatorKind::Call {
120+
func: mir::Operand::Constant(mir::Constant {
121+
literal: mir::Literal::Item { def_id, .. }, ..
122+
}),
123+
ref args, ..
124+
} if Some(def_id) == self.bcx.tcx().lang_items.box_free_fn() => {
125+
// box_free(x) shares with `drop x` the property that it
126+
// is not guaranteed to be statically dominated by the
127+
// definition of x, so x must always be in an alloca.
128+
if let mir::Operand::Consume(ref lvalue) = args[0] {
129+
self.visit_lvalue(lvalue, LvalueContext::Drop);
130+
}
131+
}
132+
_ => {}
133+
}
134+
135+
self.super_terminator_kind(block, kind);
136+
}
137+
112138
fn visit_lvalue(&mut self,
113139
lvalue: &mir::Lvalue<'tcx>,
114140
context: LvalueContext) {
115141
debug!("visit_lvalue(lvalue={:?}, context={:?})", lvalue, context);
116142

117143
// Allow uses of projections of immediate pair fields.
118144
if let mir::Lvalue::Projection(ref proj) = *lvalue {
119-
if let mir::Lvalue::Temp(temp) = proj.base {
120-
let ty = self.mir.temp_decls[temp].ty;
121-
let ty = self.bcx.monomorphize(&ty);
145+
if self.mir.local_index(&proj.base).is_some() {
146+
let ty = self.mir.lvalue_ty(self.bcx.tcx(), &proj.base);
147+
let ty = self.bcx.monomorphize(&ty.to_ty(self.bcx.tcx()));
122148
if common::type_is_imm_pair(self.bcx.ccx(), ty) {
123149
if let mir::ProjectionElem::Field(..) = proj.elem {
124150
if let LvalueContext::Consume = context {
@@ -129,25 +155,36 @@ impl<'mir, 'bcx, 'tcx> Visitor<'tcx> for TempAnalyzer<'mir, 'bcx, 'tcx> {
129155
}
130156
}
131157

132-
match *lvalue {
133-
mir::Lvalue::Temp(temp) => {
134-
match context {
135-
LvalueContext::Call => {
136-
self.mark_assigned(temp.index());
137-
}
138-
LvalueContext::Consume => {
139-
}
140-
LvalueContext::Store |
141-
LvalueContext::Drop |
142-
LvalueContext::Inspect |
143-
LvalueContext::Borrow { .. } |
144-
LvalueContext::Slice { .. } |
145-
LvalueContext::Projection => {
146-
self.mark_as_lvalue(temp.index());
158+
if let Some(index) = self.mir.local_index(lvalue) {
159+
match context {
160+
LvalueContext::Call => {
161+
self.mark_assigned(index);
162+
}
163+
LvalueContext::Consume => {
164+
}
165+
LvalueContext::Store |
166+
LvalueContext::Inspect |
167+
LvalueContext::Borrow { .. } |
168+
LvalueContext::Slice { .. } |
169+
LvalueContext::Projection => {
170+
self.mark_as_lvalue(index);
171+
}
172+
LvalueContext::Drop => {
173+
let ty = self.mir.lvalue_ty(self.bcx.tcx(), lvalue);
174+
let ty = self.bcx.monomorphize(&ty.to_ty(self.bcx.tcx()));
175+
176+
// Only need the lvalue if we're actually dropping it.
177+
if glue::type_needs_drop(self.bcx.tcx(), ty) {
178+
self.mark_as_lvalue(index);
147179
}
148180
}
149181
}
150-
_ => {
182+
}
183+
184+
// A deref projection only reads the pointer, never needs the lvalue.
185+
if let mir::Lvalue::Projection(ref proj) = *lvalue {
186+
if let mir::ProjectionElem::Deref = proj.elem {
187+
return self.visit_lvalue(&proj.base, LvalueContext::Consume);
151188
}
152189
}
153190

0 commit comments

Comments
 (0)