Skip to content

Commit 432460a

Browse files
committed
Synthesize calls to box_free language item
This gets rid of Drop(Free, _) MIR construct by synthesizing a call to language item which takes care of dropping instead.
1 parent 7b9d6d3 commit 432460a

File tree

20 files changed

+127
-69
lines changed

20 files changed

+127
-69
lines changed

src/doc/book/lang-items.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,17 @@ unsafe fn allocate(size: usize, _align: usize) -> *mut u8 {
3939

4040
p
4141
}
42+
4243
#[lang = "exchange_free"]
4344
unsafe fn deallocate(ptr: *mut u8, _size: usize, _align: usize) {
4445
libc::free(ptr as *mut libc::c_void)
4546
}
4647

48+
#[lang = "box_free"]
49+
unsafe fn box_free<T>(ptr: *mut T) {
50+
deallocate(ptr as *mut u8, ::core::mem::size_of::<T>(), ::core::mem::align_of::<T>());
51+
}
52+
4753
#[start]
4854
fn main(argc: isize, argv: *const *const u8) -> isize {
4955
let x = box 1;

src/liballoc/heap.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
issue = "27700")]
1717

1818
use core::{isize, usize};
19+
#[cfg(not(test))]
20+
use core::intrinsics::{size_of, min_align_of};
1921

2022
#[allow(improper_ctypes)]
2123
extern "C" {
@@ -147,6 +149,17 @@ unsafe fn exchange_free(ptr: *mut u8, old_size: usize, align: usize) {
147149
deallocate(ptr, old_size, align);
148150
}
149151

152+
#[cfg(not(test))]
153+
#[lang = "box_free"]
154+
#[inline]
155+
unsafe fn box_free<T>(ptr: *mut T) {
156+
let size = size_of::<T>();
157+
// We do not allocate for Box<T> when T is ZST, so deallocation is also not necessary.
158+
if size != 0 {
159+
deallocate(ptr as *mut u8, size, min_align_of::<T>());
160+
}
161+
}
162+
150163
#[cfg(test)]
151164
mod tests {
152165
extern crate test;

src/librustc/middle/dead.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -435,7 +435,7 @@ impl<'a, 'tcx> DeadVisitor<'a, 'tcx> {
435435
let is_named = node.name().is_some();
436436
let field_type = self.tcx.node_id_to_type(node.id);
437437
let is_marker_field = match field_type.ty_to_def_id() {
438-
Some(def_id) => self.tcx.lang_items.items().any(|(_, item)| *item == Some(def_id)),
438+
Some(def_id) => self.tcx.lang_items.items().iter().any(|item| *item == Some(def_id)),
439439
_ => false
440440
};
441441
is_named

src/librustc/middle/lang_items.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,6 @@ use syntax::parse::token::InternedString;
3636
use rustc_front::intravisit::Visitor;
3737
use rustc_front::hir;
3838

39-
use std::iter::Enumerate;
40-
use std::slice;
41-
4239
// The actual lang items defined come at the end of this file in one handy table.
4340
// So you probably just want to nip down to the end.
4441
macro_rules! lets_do_this {
@@ -69,8 +66,8 @@ impl LanguageItems {
6966
}
7067
}
7168

72-
pub fn items<'a>(&'a self) -> Enumerate<slice::Iter<'a, Option<DefId>>> {
73-
self.items.iter().enumerate()
69+
pub fn items(&self) -> &[Option<DefId>] {
70+
&*self.items
7471
}
7572

7673
pub fn item_name(index: usize) -> &'static str {
@@ -334,6 +331,7 @@ lets_do_this! {
334331

335332
ExchangeMallocFnLangItem, "exchange_malloc", exchange_malloc_fn;
336333
ExchangeFreeFnLangItem, "exchange_free", exchange_free_fn;
334+
BoxFreeFnLangItem, "box_free", box_free_fn;
337335
StrDupUniqFnLangItem, "strdup_uniq", strdup_uniq_fn;
338336

339337
StartFnLangItem, "start", start_fn;

src/librustc/middle/reachable.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -362,7 +362,7 @@ pub fn find_reachable(tcx: &ty::ctxt,
362362
for (id, _) in &access_levels.map {
363363
reachable_context.worklist.push(*id);
364364
}
365-
for (_, item) in tcx.lang_items.items() {
365+
for item in tcx.lang_items.items().iter() {
366366
if let Some(did) = *item {
367367
if let Some(node_id) = tcx.map.as_local_node_id(did) {
368368
reachable_context.worklist.push(node_id);

src/librustc/mir/repr.rs

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -490,23 +490,15 @@ pub struct Statement<'tcx> {
490490
#[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
491491
pub enum StatementKind<'tcx> {
492492
Assign(Lvalue<'tcx>, Rvalue<'tcx>),
493-
Drop(DropKind, Lvalue<'tcx>),
494-
}
495-
496-
#[derive(Copy, Clone, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable)]
497-
pub enum DropKind {
498-
/// free a partially constructed box, should go away eventually
499-
Free,
500-
Deep
493+
Drop(Lvalue<'tcx>),
501494
}
502495

503496
impl<'tcx> Debug for Statement<'tcx> {
504497
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
505498
use self::StatementKind::*;
506499
match self.kind {
507500
Assign(ref lv, ref rv) => write!(fmt, "{:?} = {:?}", lv, rv),
508-
Drop(DropKind::Free, ref lv) => write!(fmt, "free {:?}", lv),
509-
Drop(DropKind::Deep, ref lv) => write!(fmt, "drop {:?}", lv),
501+
Drop(ref lv) => write!(fmt, "drop {:?}", lv),
510502
}
511503
}
512504
}

src/librustc/mir/visit.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ macro_rules! make_mir_visitor {
124124
ref $($mutability)* rvalue) => {
125125
self.visit_assign(block, lvalue, rvalue);
126126
}
127-
StatementKind::Drop(_, ref $($mutability)* lvalue) => {
127+
StatementKind::Drop(ref $($mutability)* lvalue) => {
128128
self.visit_lvalue(lvalue, LvalueContext::Drop);
129129
}
130130
}

src/librustc_metadata/encoder.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1649,7 +1649,7 @@ fn encode_crate_deps(rbml_w: &mut Encoder, cstore: &cstore::CStore) {
16491649
fn encode_lang_items(ecx: &EncodeContext, rbml_w: &mut Encoder) {
16501650
rbml_w.start_tag(tag_lang_items);
16511651

1652-
for (i, &opt_def_id) in ecx.tcx.lang_items.items() {
1652+
for (i, &opt_def_id) in ecx.tcx.lang_items.items().iter().enumerate() {
16531653
if let Some(def_id) = opt_def_id {
16541654
if def_id.is_local() {
16551655
rbml_w.start_tag(tag_lang_items_item);

src/librustc_mir/build/cfg.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,21 @@ impl<'tcx> CFG<'tcx> {
3232
BasicBlock::new(node_index)
3333
}
3434

35+
pub fn start_new_cleanup_block(&mut self) -> BasicBlock {
36+
let bb = self.start_new_block();
37+
self.block_data_mut(bb).is_cleanup = true;
38+
bb
39+
}
40+
3541
pub fn push(&mut self, block: BasicBlock, statement: Statement<'tcx>) {
3642
debug!("push({:?}, {:?})", block, statement);
3743
self.block_data_mut(block).statements.push(statement);
3844
}
3945

40-
pub fn push_drop(&mut self, block: BasicBlock, span: Span,
41-
kind: DropKind, lvalue: &Lvalue<'tcx>) {
46+
pub fn push_drop(&mut self, block: BasicBlock, span: Span, lvalue: &Lvalue<'tcx>) {
4247
self.push(block, Statement {
4348
span: span,
44-
kind: StatementKind::Drop(kind, lvalue.clone())
49+
kind: StatementKind::Drop(lvalue.clone())
4550
});
4651
}
4752

src/librustc_mir/build/expr/as_rvalue.rs

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -59,25 +59,18 @@ impl<'a,'tcx> Builder<'a,'tcx> {
5959
let arg = unpack!(block = this.as_operand(block, arg));
6060
block.and(Rvalue::UnaryOp(op, arg))
6161
}
62-
ExprKind::Box { value } => {
62+
ExprKind::Box { value, value_extents } => {
6363
let value = this.hir.mirror(value);
6464
let result = this.temp(expr.ty);
65-
6665
// to start, malloc some memory of suitable type (thus far, uninitialized):
67-
let rvalue = Rvalue::Box(value.ty);
68-
this.cfg.push_assign(block, expr_span, &result, rvalue);
69-
70-
// schedule a shallow free of that memory, lest we unwind:
71-
let extent = this.extent_of_innermost_scope();
72-
this.schedule_drop(expr_span, extent, DropKind::Free, &result, value.ty);
73-
74-
// initialize the box contents:
75-
let contents = result.clone().deref();
76-
unpack!(block = this.into(&contents, block, value));
77-
78-
// now that the result is fully initialized, cancel the drop
79-
// by "using" the result (which is linear):
80-
block.and(Rvalue::Use(Operand::Consume(result)))
66+
this.cfg.push_assign(block, expr_span, &result, Rvalue::Box(value.ty));
67+
this.in_scope(value_extents, block, |this| {
68+
// schedule a shallow free of that memory, lest we unwind:
69+
this.schedule_box_free(expr_span, value_extents, &result, value.ty);
70+
// initialize the box contents:
71+
unpack!(block = this.into(&result.clone().deref(), block, value));
72+
block.and(Rvalue::Use(Operand::Consume(result)))
73+
})
8174
}
8275
ExprKind::Cast { source } => {
8376
let source = unpack!(block = this.as_operand(block, source));

src/librustc_mir/build/expr/as_temp.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ impl<'a,'tcx> Builder<'a,'tcx> {
4141
this.hir.span_bug(expr.span, "no temp_lifetime for expr");
4242
}
4343
};
44-
this.schedule_drop(expr.span, temp_lifetime, DropKind::Deep, &temp, expr_ty);
44+
this.schedule_drop(expr.span, temp_lifetime, &temp, expr_ty);
4545

4646
// Careful here not to cause an infinite cycle. If we always
4747
// called `into`, then for lvalues like `x.f`, it would

src/librustc_mir/build/expr/into.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ impl<'a,'tcx> Builder<'a,'tcx> {
188188
// operators like x[j] = x[i].
189189
let rhs = unpack!(block = this.as_operand(block, rhs));
190190
let lhs = unpack!(block = this.as_lvalue(block, lhs));
191-
this.cfg.push_drop(block, expr_span, DropKind::Deep, &lhs);
191+
this.cfg.push_drop(block, expr_span, &lhs);
192192
this.cfg.push_assign(block, expr_span, &lhs, Rvalue::Use(rhs));
193193
block.unit()
194194
}

src/librustc_mir/build/matches/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -601,7 +601,7 @@ impl<'a,'tcx> Builder<'a,'tcx> {
601601
ty: var_ty.clone(),
602602
});
603603
let index = index as u32;
604-
self.schedule_drop(span, var_extent, DropKind::Deep, &Lvalue::Var(index), var_ty);
604+
self.schedule_drop(span, var_extent, &Lvalue::Var(index), var_ty);
605605
self.var_indices.insert(var_id, index);
606606

607607
debug!("declare_binding: index={:?}", index);

src/librustc_mir/build/scope.rs

Lines changed: 69 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -89,15 +89,16 @@ should go to.
8989
use build::{BlockAnd, BlockAndExtension, Builder};
9090
use rustc::middle::region::CodeExtent;
9191
use rustc::middle::lang_items;
92-
use rustc::middle::subst::Substs;
92+
use rustc::middle::subst::{Substs, VecPerParamSpace};
9393
use rustc::middle::ty::{self, Ty};
9494
use rustc::mir::repr::*;
9595
use syntax::codemap::{Span, DUMMY_SP};
9696
use syntax::parse::token::intern_and_get_ident;
9797

9898
pub struct Scope<'tcx> {
9999
extent: CodeExtent,
100-
drops: Vec<(DropKind, Span, Lvalue<'tcx>)>,
100+
drops: Vec<(Span, Lvalue<'tcx>)>,
101+
frees: Vec<(Span, Lvalue<'tcx>, Ty<'tcx>)>,
101102
cached_block: Option<BasicBlock>,
102103
}
103104

@@ -164,6 +165,7 @@ impl<'a,'tcx> Builder<'a,'tcx> {
164165
self.scopes.push(Scope {
165166
extent: extent.clone(),
166167
drops: vec![],
168+
frees: vec![],
167169
cached_block: None,
168170
});
169171
}
@@ -180,8 +182,8 @@ impl<'a,'tcx> Builder<'a,'tcx> {
180182
// add in any drops needed on the fallthrough path (any other
181183
// exiting paths, such as those that arise from `break`, will
182184
// have drops already)
183-
for (kind, span, lvalue) in scope.drops {
184-
self.cfg.push_drop(block, span, kind, &lvalue);
185+
for (span, lvalue) in scope.drops {
186+
self.cfg.push_drop(block, span, &lvalue);
185187
}
186188
}
187189

@@ -225,8 +227,8 @@ impl<'a,'tcx> Builder<'a,'tcx> {
225227
});
226228

227229
for scope in scopes.iter_mut().rev().take(scope_count) {
228-
for &(kind, drop_span, ref lvalue) in &scope.drops {
229-
cfg.push_drop(block, drop_span, kind, lvalue);
230+
for &(drop_span, ref lvalue) in &scope.drops {
231+
cfg.push_drop(block, drop_span, lvalue);
230232
}
231233
}
232234
cfg.terminate(block, Terminator::Goto { target: target });
@@ -242,23 +244,55 @@ impl<'a,'tcx> Builder<'a,'tcx> {
242244
return None;
243245
}
244246

247+
let tcx = self.hir.tcx();
248+
let unit_tmp = self.get_unit_temp();
245249
let mut terminator = Terminator::Resume;
246250
// Given an array of scopes, we generate these from the outermost scope to the innermost
247251
// one. Thus for array [S0, S1, S2] with corresponding cleanup blocks [B0, B1, B2], we will
248252
// generate B0 <- B1 <- B2 in left-to-right order. The outermost scope (B0) will always
249253
// terminate with a Resume terminator.
250-
for scope in self.scopes.iter_mut().filter(|s| !s.drops.is_empty()) {
254+
for scope in self.scopes.iter_mut().filter(|s| !s.drops.is_empty() || !s.frees.is_empty()) {
251255
if let Some(b) = scope.cached_block {
252256
terminator = Terminator::Goto { target: b };
253257
continue;
254258
} else {
255-
let new_block = self.cfg.start_new_block();
256-
self.cfg.block_data_mut(new_block).is_cleanup = true;
259+
let mut new_block = self.cfg.start_new_cleanup_block();
257260
self.cfg.terminate(new_block, terminator);
258261
terminator = Terminator::Goto { target: new_block };
259-
for &(kind, span, ref lvalue) in scope.drops.iter().rev() {
260-
self.cfg.push_drop(new_block, span, kind, lvalue);
262+
263+
for &(span, ref lvalue) in scope.drops.iter().rev() {
264+
self.cfg.push_drop(new_block, span, lvalue);
265+
}
266+
267+
for &(_, ref lvalue, ref item_ty) in scope.frees.iter().rev() {
268+
let item = lang_items::SpannedLangItems::box_free_fn(&tcx.lang_items)
269+
.expect("box_free language item required");
270+
let substs = tcx.mk_substs(Substs::new(
271+
VecPerParamSpace::new(vec![], vec![], vec![item_ty]),
272+
VecPerParamSpace::new(vec![], vec![], vec![])
273+
));
274+
let func = Constant {
275+
span: item.1,
276+
ty: tcx.lookup_item_type(item.0).ty.subst(substs),
277+
literal: Literal::Item {
278+
def_id: item.0,
279+
kind: ItemKind::Function,
280+
substs: substs
281+
}
282+
};
283+
let old_block = new_block;
284+
new_block = self.cfg.start_new_cleanup_block();
285+
self.cfg.terminate(new_block, Terminator::Call {
286+
func: Operand::Constant(func),
287+
args: vec![Operand::Consume(lvalue.clone())],
288+
kind: CallKind::Converging {
289+
target: old_block,
290+
destination: unit_tmp.clone()
291+
}
292+
});
293+
terminator = Terminator::Goto { target: new_block };
261294
}
295+
262296
scope.cached_block = Some(new_block);
263297
}
264298
}
@@ -272,7 +306,6 @@ impl<'a,'tcx> Builder<'a,'tcx> {
272306
pub fn schedule_drop(&mut self,
273307
span: Span,
274308
extent: CodeExtent,
275-
kind: DropKind,
276309
lvalue: &Lvalue<'tcx>,
277310
lvalue_ty: Ty<'tcx>) {
278311
if self.hir.needs_drop(lvalue_ty) {
@@ -282,7 +315,7 @@ impl<'a,'tcx> Builder<'a,'tcx> {
282315
// incorrect (i.e. they still are pointing at old cached_block).
283316
scope.cached_block = None;
284317
if scope.extent == extent {
285-
scope.drops.push((kind, span, lvalue.clone()));
318+
scope.drops.push((span, lvalue.clone()));
286319
return;
287320
}
288321
}
@@ -291,6 +324,27 @@ impl<'a,'tcx> Builder<'a,'tcx> {
291324
}
292325
}
293326

327+
/// Schedule dropping of a not yet fully initialised box. This cleanup will (and should) only
328+
/// be translated into unwind branch. The extent should be for the `EXPR` inside `box EXPR`.
329+
pub fn schedule_box_free(&mut self,
330+
span: Span,
331+
extent: CodeExtent,
332+
lvalue: &Lvalue<'tcx>,
333+
item_ty: Ty<'tcx>) {
334+
for scope in self.scopes.iter_mut().rev() {
335+
// We must invalidate all the cached_blocks leading up to the scope we’re looking
336+
// for, because otherwise some/most of the blocks in the chain might become
337+
// incorrect (i.e. they still are pointing at old cached_block).
338+
scope.cached_block = None;
339+
if scope.extent == extent {
340+
scope.frees.push((span, lvalue.clone(), item_ty));
341+
return;
342+
}
343+
}
344+
self.hir.span_bug(span,
345+
&format!("extent {:?} not in scope to drop {:?}", extent, lvalue));
346+
}
347+
294348
pub fn extent_of_innermost_scope(&self) -> CodeExtent {
295349
self.scopes.last().map(|scope| scope.extent).unwrap()
296350
}
@@ -299,6 +353,7 @@ impl<'a,'tcx> Builder<'a,'tcx> {
299353
self.scopes.first().map(|scope| scope.extent).unwrap()
300354
}
301355

356+
302357
pub fn panic_bounds_check(&mut self,
303358
block: BasicBlock,
304359
index: Operand<'tcx>,
@@ -405,4 +460,5 @@ impl<'a,'tcx> Builder<'a,'tcx> {
405460
literal: self.hir.usize_literal(span_lines.line)
406461
})
407462
}
463+
408464
}

0 commit comments

Comments
 (0)