Skip to content

Commit 54dbd0b

Browse files
committed
librustc_trans: Remove misoptimization in treating derefs of Box as rvalues.
1 parent 720735b commit 54dbd0b

File tree

4 files changed

+60
-61
lines changed

4 files changed

+60
-61
lines changed

src/librustc_trans/trans/datum.rs

-11
Original file line numberDiff line numberDiff line change
@@ -74,17 +74,6 @@
7474
//! `&foo()` or `match foo() { ref x => ... }`, where the user is
7575
//! implicitly requesting a temporary.
7676
//!
77-
//! Somewhat surprisingly, not all lvalue expressions yield lvalue datums
78-
//! when trans'd. Ultimately the reason for this is to micro-optimize
79-
//! the resulting LLVM. For example, consider the following code:
80-
//!
81-
//! fn foo() -> Box<int> { ... }
82-
//! let x = *foo();
83-
//!
84-
//! The expression `*foo()` is an lvalue, but if you invoke `expr::trans`,
85-
//! it will return an rvalue datum. See `deref_once` in expr.rs for
86-
//! more details.
87-
//!
8877
//! ### Rvalues in detail
8978
//!
9079
//! Rvalues datums are values with no cleanup scheduled. One must be

src/librustc_trans/trans/expr.rs

+7-50
Original file line numberDiff line numberDiff line change
@@ -2247,16 +2247,20 @@ fn deref_once<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
22472247

22482248
let r = match datum.ty.sty {
22492249
ty::ty_uniq(content_ty) => {
2250+
// Make sure we have an lvalue datum here to get the
2251+
// proper cleanups scheduled
2252+
let datum = unpack_datum!(
2253+
bcx, datum.to_lvalue_datum(bcx, "deref", expr.id));
2254+
22502255
if type_is_sized(bcx.tcx(), content_ty) {
2251-
deref_owned_pointer(bcx, expr, datum, content_ty)
2256+
let ptr = load_ty(bcx, datum.val, datum.ty);
2257+
DatumBlock::new(bcx, Datum::new(ptr, content_ty, LvalueExpr))
22522258
} else {
22532259
// A fat pointer and a DST lvalue have the same representation
22542260
// just different types. Since there is no temporary for `*e`
22552261
// here (because it is unsized), we cannot emulate the sized
22562262
// object code path for running drop glue and free. Instead,
22572263
// we schedule cleanup for `e`, turning it into an lvalue.
2258-
let datum = unpack_datum!(
2259-
bcx, datum.to_lvalue_datum(bcx, "deref", expr.id));
22602264

22612265
let datum = Datum::new(datum.val, content_ty, LvalueExpr);
22622266
DatumBlock::new(bcx, datum)
@@ -2293,53 +2297,6 @@ fn deref_once<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
22932297
expr.id, method_call, r.datum.to_string(ccx));
22942298

22952299
return r;
2296-
2297-
/// We microoptimize derefs of owned pointers a bit here. Basically, the idea is to make the
2298-
/// deref of an rvalue result in an rvalue. This helps to avoid intermediate stack slots in the
2299-
/// resulting LLVM. The idea here is that, if the `Box<T>` pointer is an rvalue, then we can
2300-
/// schedule a *shallow* free of the `Box<T>` pointer, and then return a ByRef rvalue into the
2301-
/// pointer. Because the free is shallow, it is legit to return an rvalue, because we know that
2302-
/// the contents are not yet scheduled to be freed. The language rules ensure that the contents
2303-
/// will be used (or moved) before the free occurs.
2304-
fn deref_owned_pointer<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
2305-
expr: &ast::Expr,
2306-
datum: Datum<'tcx, Expr>,
2307-
content_ty: Ty<'tcx>)
2308-
-> DatumBlock<'blk, 'tcx, Expr> {
2309-
match datum.kind {
2310-
RvalueExpr(Rvalue { mode: ByRef }) => {
2311-
let scope = cleanup::temporary_scope(bcx.tcx(), expr.id);
2312-
let ptr = Load(bcx, datum.val);
2313-
if !type_is_zero_size(bcx.ccx(), content_ty) {
2314-
bcx.fcx.schedule_free_value(scope, ptr, cleanup::HeapExchange, content_ty);
2315-
}
2316-
}
2317-
RvalueExpr(Rvalue { mode: ByValue }) => {
2318-
let scope = cleanup::temporary_scope(bcx.tcx(), expr.id);
2319-
if !type_is_zero_size(bcx.ccx(), content_ty) {
2320-
bcx.fcx.schedule_free_value(scope, datum.val, cleanup::HeapExchange,
2321-
content_ty);
2322-
}
2323-
}
2324-
LvalueExpr => { }
2325-
}
2326-
2327-
// If we had an rvalue in, we produce an rvalue out.
2328-
let (llptr, kind) = match datum.kind {
2329-
LvalueExpr => {
2330-
(Load(bcx, datum.val), LvalueExpr)
2331-
}
2332-
RvalueExpr(Rvalue { mode: ByRef }) => {
2333-
(Load(bcx, datum.val), RvalueExpr(Rvalue::new(ByRef)))
2334-
}
2335-
RvalueExpr(Rvalue { mode: ByValue }) => {
2336-
(datum.val, RvalueExpr(Rvalue::new(ByRef)))
2337-
}
2338-
};
2339-
2340-
let datum = Datum { ty: content_ty, val: llptr, kind: kind };
2341-
DatumBlock { bcx: bcx, datum: datum }
2342-
}
23432300
}
23442301

23452302
#[derive(Debug)]

src/test/run-pass/issue-18845.rs

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Copyright 2015 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+
// This used to generate invalid IR in that even if we took the
12+
// `false` branch we'd still try to free the Box from the other
13+
// arm. This was due to treating `*Box::new(9)` as an rvalue datum
14+
// instead of as an lvalue.
15+
16+
fn test(foo: bool) -> u8 {
17+
match foo {
18+
true => *Box::new(9),
19+
false => 0
20+
}
21+
}
22+
23+
fn main() {
24+
assert_eq!(9, test(true));
25+
}

src/test/run-pass/issue-25497.rs

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright 2015 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+
#[derive(Clone, Debug, PartialEq)]
12+
enum Expression {
13+
Dummy,
14+
Add(Box<Expression>),
15+
}
16+
17+
use Expression::*;
18+
19+
fn simplify(exp: Expression) -> Expression {
20+
match exp {
21+
Add(n) => *n.clone(),
22+
_ => Dummy
23+
}
24+
}
25+
26+
fn main() {
27+
assert_eq!(simplify(Add(Box::new(Dummy))), Dummy);
28+
}

0 commit comments

Comments
 (0)