|
15 | 15 |
|
16 | 16 | use rustc::mir::{Constant, Literal, Location, Place, Mir, Operand, Rvalue, Local};
|
17 | 17 | use rustc::mir::{NullOp, StatementKind, Statement, BasicBlock, LocalKind};
|
18 |
| -use rustc::mir::{TerminatorKind, ClearCrossCrate, SourceInfo, BinOp}; |
19 |
| -use rustc::mir::visit::Visitor; |
| 18 | +use rustc::mir::{TerminatorKind, ClearCrossCrate, SourceInfo, BinOp, BorrowKind}; |
| 19 | +use rustc::mir::visit::{Visitor, PlaceContext}; |
20 | 20 | use rustc::ty::layout::LayoutOf;
|
21 | 21 | use rustc::middle::const_val::ConstVal;
|
22 | 22 | use rustc::ty::{TyCtxt, self, Instance};
|
@@ -64,11 +64,16 @@ impl<'b, 'a, 'tcx:'b> OptimizationFinder<'b, 'a, 'tcx> {
|
64 | 64 | tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
65 | 65 | source: MirSource,
|
66 | 66 | ) -> OptimizationFinder<'b, 'a, 'tcx> {
|
| 67 | + let can_const_prop = CanConstProp::check( |
| 68 | + mir, |
| 69 | + tcx, |
| 70 | + tcx.param_env(source.def_id), |
| 71 | + ); |
67 | 72 | OptimizationFinder {
|
68 | 73 | mir,
|
69 | 74 | tcx,
|
70 | 75 | source,
|
71 |
| - can_const_prop: CanConstProp::check(mir), |
| 76 | + can_const_prop, |
72 | 77 | places: IndexVec::from_elem(None, &mir.local_decls),
|
73 | 78 | }
|
74 | 79 | }
|
@@ -272,78 +277,71 @@ fn type_size_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
272 | 277 | (tcx, param_env).layout_of(ty).ok().map(|layout| layout.size.bytes())
|
273 | 278 | }
|
274 | 279 |
|
275 |
| -struct CanConstProp { |
| 280 | +struct CanConstProp<'b, 'a, 'tcx:'a+'b> { |
276 | 281 | can_const_prop: IndexVec<Local, bool>,
|
277 | 282 | // false at the beginning, once set, there are not allowed to be any more assignments
|
278 | 283 | found_assignment: IndexVec<Local, bool>,
|
| 284 | + mir: &'b Mir<'tcx>, |
| 285 | + tcx: TyCtxt<'a, 'tcx, 'tcx>, |
| 286 | + param_env: ty::ParamEnv<'tcx>, |
279 | 287 | }
|
280 | 288 |
|
281 |
| -impl CanConstProp { |
| 289 | +impl<'b, 'a, 'tcx:'b> CanConstProp<'b, 'a, 'tcx> { |
282 | 290 | /// returns true if `local` can be propagated
|
283 |
| - fn check<'tcx>(mir: &Mir<'tcx>) -> IndexVec<Local, bool> { |
| 291 | + fn check( |
| 292 | + mir: &'b Mir<'tcx>, |
| 293 | + tcx: TyCtxt<'a, 'tcx, 'tcx>, |
| 294 | + param_env: ty::ParamEnv<'tcx>, |
| 295 | + ) -> IndexVec<Local, bool> { |
284 | 296 | let mut cpv = CanConstProp {
|
285 | 297 | can_const_prop: IndexVec::from_elem(true, &mir.local_decls),
|
286 | 298 | found_assignment: IndexVec::from_elem(false, &mir.local_decls),
|
| 299 | + mir, |
| 300 | + tcx, |
| 301 | + param_env, |
287 | 302 | };
|
288 | 303 | for (local, val) in cpv.can_const_prop.iter_enumerated_mut() {
|
289 |
| - *val = mir.local_kind(local) == LocalKind::Temp; |
| 304 | + *val = mir.local_kind(local) != LocalKind::Arg; |
290 | 305 | }
|
291 | 306 | cpv.visit_mir(mir);
|
292 | 307 | cpv.can_const_prop
|
293 | 308 | }
|
294 | 309 | }
|
295 | 310 |
|
296 |
| -fn place_to_local(mut place: &Place) -> Option<Local> { |
297 |
| - while let Place::Projection(ref proj) = place { |
298 |
| - place = &proj.base; |
299 |
| - } |
300 |
| - if let Place::Local(local) = *place { |
301 |
| - Some(local) |
302 |
| - } else { |
303 |
| - None |
304 |
| - } |
305 |
| -} |
306 |
| - |
307 |
| -impl<'tcx> Visitor<'tcx> for CanConstProp { |
308 |
| - fn visit_statement( |
| 311 | +impl<'a, 'b, 'tcx> Visitor<'tcx> for CanConstProp<'a, 'b, 'tcx> { |
| 312 | + fn visit_local( |
309 | 313 | &mut self,
|
310 |
| - block: BasicBlock, |
311 |
| - statement: &Statement<'tcx>, |
312 |
| - location: Location, |
| 314 | + &local: &Local, |
| 315 | + context: PlaceContext<'tcx>, |
| 316 | + _: Location, |
313 | 317 | ) {
|
314 |
| - self.super_statement(block, statement, location); |
315 |
| - match statement.kind { |
316 |
| - StatementKind::SetDiscriminant { ref place, .. } | |
317 |
| - StatementKind::Assign(ref place, _) => { |
318 |
| - if let Some(local) = place_to_local(place) { |
319 |
| - if self.found_assignment[local] { |
320 |
| - self.can_const_prop[local] = false; |
321 |
| - } else { |
322 |
| - self.found_assignment[local] = true |
323 |
| - } |
324 |
| - } |
| 318 | + use rustc::mir::visit::PlaceContext::*; |
| 319 | + match context { |
| 320 | + // Constants must have at most one write |
| 321 | + // FIXME(oli-obk): we could be more powerful here, if the multiple writes |
| 322 | + // only occur in independent execution paths |
| 323 | + Store => if self.found_assignment[local] { |
| 324 | + self.can_const_prop[local] = false; |
| 325 | + } else { |
| 326 | + self.found_assignment[local] = true |
325 | 327 | },
|
326 |
| - StatementKind::InlineAsm { ref outputs, .. } => { |
327 |
| - for place in outputs { |
328 |
| - if let Some(local) = place_to_local(place) { |
329 |
| - if self.found_assignment[local] { |
330 |
| - self.can_const_prop[local] = false; |
331 |
| - } else { |
332 |
| - self.found_assignment[local] = true |
333 |
| - } |
334 |
| - return; |
335 |
| - } |
| 328 | + // Reading constants is allowed an arbitrary number of times |
| 329 | + Copy | Move | |
| 330 | + StorageDead | StorageLive | |
| 331 | + Validate | |
| 332 | + Inspect => {}, |
| 333 | + Borrow { kind: BorrowKind::Shared, .. } => { |
| 334 | + // cannot const prop immutable borrows of types with interior mutability |
| 335 | + let has_interior_mutability = self |
| 336 | + .mir |
| 337 | + .local_decls[local] |
| 338 | + .ty |
| 339 | + .is_freeze(self.tcx, self.param_env, self.mir.span); |
| 340 | + if has_interior_mutability { |
| 341 | + self.can_const_prop[local] = false; |
336 | 342 | }
|
337 | 343 | }
|
338 |
| - _ => {} |
339 |
| - } |
340 |
| - } |
341 |
| - fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { |
342 |
| - self.super_rvalue(rvalue, location); |
343 |
| - if let Rvalue::Ref(_, _, ref place) = *rvalue { |
344 |
| - if let Some(local) = place_to_local(place) { |
345 |
| - self.can_const_prop[local] = false; |
346 |
| - } |
| 344 | + _ => self.can_const_prop[local] = false, |
347 | 345 | }
|
348 | 346 | }
|
349 | 347 | }
|
|
0 commit comments