Skip to content

Commit 6a5b263

Browse files
committed
Add (and use) an analysis to determine which temps can forgo an alloca.
1 parent 544b06d commit 6a5b263

File tree

4 files changed

+160
-25
lines changed

4 files changed

+160
-25
lines changed
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
//! An analysis to determine which temporaries require allocas and
12+
//! which do not.
13+
14+
use rustc_data_structures::fnv::FnvHashSet;
15+
use rustc_mir::repr as mir;
16+
use rustc_mir::visit::{Visitor, LvalueContext};
17+
use trans::common::{self, Block};
18+
use super::rvalue;
19+
20+
pub fn lvalue_temps<'bcx,'tcx>(bcx: Block<'bcx,'tcx>,
21+
mir: &mir::Mir<'tcx>)
22+
-> FnvHashSet<usize> {
23+
let mut analyzer = TempAnalyzer::new();
24+
25+
analyzer.visit_mir(mir);
26+
27+
for (index, temp_decl) in mir.temp_decls.iter().enumerate() {
28+
let ty = bcx.monomorphize(&temp_decl.ty);
29+
debug!("temp {:?} has type {:?}", index, ty);
30+
if
31+
ty.is_scalar() ||
32+
ty.is_unique() ||
33+
ty.is_region_ptr() ||
34+
ty.is_simd()
35+
{
36+
// These sorts of types are immediates that we can store
37+
// in an ValueRef without an alloca.
38+
assert!(common::type_is_immediate(bcx.ccx(), ty));
39+
} else {
40+
// These sorts of types require an alloca. Note that
41+
// type_is_immediate() may *still* be true, particularly
42+
// for newtypes, but we currently force some types
43+
// (e.g. structs) into an alloca unconditionally, just so
44+
// that we don't have to deal with having two pathways
45+
// (gep vs getvalue etc).
46+
analyzer.mark_as_lvalue(index);
47+
}
48+
}
49+
50+
analyzer.lvalue_temps
51+
}
52+
53+
struct TempAnalyzer {
54+
lvalue_temps: FnvHashSet<usize>,
55+
}
56+
57+
impl TempAnalyzer {
58+
fn new() -> TempAnalyzer {
59+
TempAnalyzer { lvalue_temps: FnvHashSet() }
60+
}
61+
62+
fn mark_as_lvalue(&mut self, temp: usize) {
63+
debug!("marking temp {} as lvalue", temp);
64+
self.lvalue_temps.insert(temp);
65+
}
66+
}
67+
68+
impl<'tcx> Visitor<'tcx> for TempAnalyzer {
69+
fn visit_assign(&mut self,
70+
block: mir::BasicBlock,
71+
lvalue: &mir::Lvalue<'tcx>,
72+
rvalue: &mir::Rvalue<'tcx>) {
73+
debug!("visit_assign(block={:?}, lvalue={:?}, rvalue={:?})", block, lvalue, rvalue);
74+
75+
match *lvalue {
76+
mir::Lvalue::Temp(index) => {
77+
if !rvalue::rvalue_creates_operand(rvalue) {
78+
self.mark_as_lvalue(index as usize);
79+
}
80+
}
81+
_ => {
82+
self.visit_lvalue(lvalue, LvalueContext::Store);
83+
}
84+
}
85+
86+
self.visit_rvalue(rvalue);
87+
}
88+
89+
fn visit_lvalue(&mut self,
90+
lvalue: &mir::Lvalue<'tcx>,
91+
context: LvalueContext) {
92+
debug!("visit_lvalue(lvalue={:?}, context={:?})", lvalue, context);
93+
94+
match *lvalue {
95+
mir::Lvalue::Temp(index) => {
96+
match context {
97+
LvalueContext::Consume => {
98+
}
99+
LvalueContext::Store |
100+
LvalueContext::Drop |
101+
LvalueContext::Inspect |
102+
LvalueContext::Borrow { .. } |
103+
LvalueContext::Slice { .. } |
104+
LvalueContext::Projection => {
105+
self.mark_as_lvalue(index as usize);
106+
}
107+
}
108+
}
109+
_ => {
110+
}
111+
}
112+
113+
self.super_lvalue(lvalue, context);
114+
}
115+
}

src/librustc_trans/trans/mir/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010

1111
use libc::c_uint;
1212
use llvm::{self, ValueRef};
13-
use rustc_data_structures::fnv::FnvHashSet;
1413
use rustc_mir::repr as mir;
1514
use rustc_mir::tcx::LvalueTy;
1615
use trans::base;
@@ -79,7 +78,7 @@ pub fn trans_mir<'bcx, 'tcx>(bcx: Block<'bcx, 'tcx>) {
7978

8079
// Analyze the temps to determine which must be lvalues
8180
// FIXME
82-
let lvalue_temps: FnvHashSet<usize> = (0..mir.temp_decls.len()).collect();
81+
let lvalue_temps = analyze::lvalue_temps(bcx, mir);
8382

8483
// Allocate variable and temp allocas
8584
let vars = mir.var_decls.iter()
@@ -183,6 +182,7 @@ fn arg_value_refs<'bcx, 'tcx>(bcx: Block<'bcx, 'tcx>,
183182
.collect()
184183
}
185184

185+
mod analyze;
186186
mod block;
187187
mod constant;
188188
mod lvalue;

src/librustc_trans/trans/mir/operand.rs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@ use trans::build;
1616
use trans::common::Block;
1717
use trans::datum;
1818

19-
use super::MirContext;
19+
use super::{MirContext, TempRef};
2020

21+
#[derive(Copy, Clone)]
2122
pub struct OperandRef<'tcx> {
2223
// This will be "indirect" if `appropriate_rvalue_mode` returns
2324
// ByRef, and otherwise ByValue.
@@ -37,6 +38,25 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
3738

3839
match *operand {
3940
mir::Operand::Consume(ref lvalue) => {
41+
// watch out for temporaries that do not have an
42+
// alloca; they are handled somewhat differently
43+
if let &mir::Lvalue::Temp(index) = lvalue {
44+
match self.temps[index as usize] {
45+
TempRef::Operand(Some(o)) => {
46+
return o;
47+
}
48+
TempRef::Operand(None) => {
49+
bcx.tcx().sess.bug(
50+
&format!("use of {:?} before def", lvalue));
51+
}
52+
TempRef::Lvalue(..) => {
53+
// use path below
54+
}
55+
}
56+
}
57+
58+
// for most lvalues, to consume them we just load them
59+
// out from their home
4060
let tr_lvalue = self.trans_lvalue(bcx, lvalue);
4161
let ty = tr_lvalue.ty.to_ty(bcx.tcx());
4262
debug!("trans_operand: tr_lvalue={} @ {:?}",

src/librustc_trans/trans/mir/rvalue.rs

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -80,40 +80,20 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
8080
}
8181

8282
_ => {
83-
assert!(self.rvalue_creates_operand(rvalue));
83+
assert!(rvalue_creates_operand(rvalue));
8484
let (bcx, temp) = self.trans_rvalue_operand(bcx, rvalue);
8585
build::Store(bcx, temp.llval, lldest);
8686
bcx
8787
}
8888
}
8989
}
9090

91-
pub fn rvalue_creates_operand(&self, rvalue: &mir::Rvalue<'tcx>) -> bool {
92-
match *rvalue {
93-
mir::Rvalue::Use(..) | // (*)
94-
mir::Rvalue::Ref(..) |
95-
mir::Rvalue::Len(..) |
96-
mir::Rvalue::Cast(..) | // (*)
97-
mir::Rvalue::BinaryOp(..) |
98-
mir::Rvalue::UnaryOp(..) |
99-
mir::Rvalue::Box(..) =>
100-
true,
101-
mir::Rvalue::Repeat(..) |
102-
mir::Rvalue::Aggregate(..) |
103-
mir::Rvalue::Slice { .. } |
104-
mir::Rvalue::InlineAsm(..) =>
105-
false,
106-
}
107-
108-
// (*) this is only true if the type is suitable
109-
}
110-
11191
pub fn trans_rvalue_operand(&mut self,
11292
bcx: Block<'bcx, 'tcx>,
11393
rvalue: &mir::Rvalue<'tcx>)
11494
-> (Block<'bcx, 'tcx>, OperandRef<'tcx>)
11595
{
116-
assert!(self.rvalue_creates_operand(rvalue), "cannot trans {:?} to operand", rvalue);
96+
assert!(rvalue_creates_operand(rvalue), "cannot trans {:?} to operand", rvalue);
11797

11898
match *rvalue {
11999
mir::Rvalue::Use(ref operand) => {
@@ -300,3 +280,23 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
300280
}
301281
}
302282
}
283+
284+
pub fn rvalue_creates_operand<'tcx>(rvalue: &mir::Rvalue<'tcx>) -> bool {
285+
match *rvalue {
286+
mir::Rvalue::Use(..) | // (*)
287+
mir::Rvalue::Ref(..) |
288+
mir::Rvalue::Len(..) |
289+
mir::Rvalue::Cast(..) | // (*)
290+
mir::Rvalue::BinaryOp(..) |
291+
mir::Rvalue::UnaryOp(..) |
292+
mir::Rvalue::Box(..) =>
293+
true,
294+
mir::Rvalue::Repeat(..) |
295+
mir::Rvalue::Aggregate(..) |
296+
mir::Rvalue::Slice { .. } |
297+
mir::Rvalue::InlineAsm(..) =>
298+
false,
299+
}
300+
301+
// (*) this is only true if the type is suitable
302+
}

0 commit comments

Comments
 (0)