Skip to content

Commit ac7cb1f

Browse files
committed
fix translation of terminators in MSVC cleanup blocks
I have taken a fairly brute force approach here: for each successor, I generate a basic block that contains a `cleanupret` to that block. It is ugly but it works. It might have been possible to play with phis, but there are rumours that there must be a 1-1 mapping between cleanup pad entries and exits, which is not the case in MIR.
1 parent 6acba65 commit ac7cb1f

File tree

4 files changed

+117
-50
lines changed

4 files changed

+117
-50
lines changed

src/librustc_trans/mir/block.rs

+40-31
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-
use llvm::{self, BasicBlockRef, ValueRef, OperandBundleDef};
11+
use llvm::{self, ValueRef, OperandBundleDef};
1212
use rustc::ty;
1313
use rustc::mir::repr as mir;
1414
use abi::{Abi, FnType, ArgType};
@@ -34,22 +34,38 @@ use super::operand::OperandValue::{self, FatPtr, Immediate, Ref};
3434

3535
impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
3636
pub fn trans_block(&mut self, bb: mir::BasicBlock) {
37-
debug!("trans_block({:?})", bb);
38-
3937
let mut bcx = self.bcx(bb);
4038
let mir = self.mir.clone();
4139
let data = mir.basic_block_data(bb);
4240

41+
debug!("trans_block({:?}={:?})", bb, data);
42+
4343
// MSVC SEH bits
4444
let (cleanup_pad, cleanup_bundle) = if let Some((cp, cb)) = self.make_cleanup_pad(bb) {
4545
(Some(cp), Some(cb))
4646
} else {
4747
(None, None)
4848
};
49-
let funclet_br = |bcx: BlockAndBuilder, llbb: BasicBlockRef| if let Some(cp) = cleanup_pad {
50-
bcx.cleanup_ret(cp, Some(llbb));
51-
} else {
52-
bcx.br(llbb);
49+
let funclet_br = |this: &Self, bcx: BlockAndBuilder, bb: mir::BasicBlock| {
50+
if let Some(cp) = cleanup_pad {
51+
bcx.cleanup_ret(cp, Some(this.blocks[bb.index()].llbb));
52+
} else {
53+
bcx.br(this.blocks[bb.index()].llbb);
54+
}
55+
};
56+
let llblock = |this: &mut Self, target: mir::BasicBlock| {
57+
let lltarget = this.blocks[target.index()].llbb;
58+
59+
if let Some(cp) = cleanup_pad {
60+
debug!("llblock: creating cleanup trampoline for {:?}", target);
61+
let name = &format!("{:?}_cleanup_trampoline_{:?}", bb, target);
62+
let block = this.fcx.new_block(name, None);
63+
let bcx = block.build();
64+
bcx.cleanup_ret(cp, Some(lltarget));
65+
block.llbb
66+
} else {
67+
lltarget
68+
}
5369
};
5470

5571
for statement in &data.statements {
@@ -78,13 +94,14 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
7894
}
7995

8096
mir::TerminatorKind::Goto { target } => {
81-
funclet_br(bcx, self.llblock(target));
97+
funclet_br(self, bcx, target);
8298
}
8399

84100
mir::TerminatorKind::If { ref cond, targets: (true_bb, false_bb) } => {
85101
let cond = self.trans_operand(&bcx, cond);
86-
let lltrue = self.llblock(true_bb);
87-
let llfalse = self.llblock(false_bb);
102+
103+
let lltrue = llblock(self, true_bb);
104+
let llfalse = llblock(self, false_bb);
88105
bcx.cond_br(cond.immediate(), lltrue, llfalse);
89106
}
90107

@@ -106,18 +123,18 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
106123
// code. This is especially helpful in cases like an if-let on a huge enum.
107124
// Note: This optimization is only valid for exhaustive matches.
108125
Some((&&bb, &c)) if c > targets.len() / 2 => {
109-
(Some(bb), self.blocks[bb.index()])
126+
(Some(bb), llblock(self, bb))
110127
}
111128
// We're generating an exhaustive switch, so the else branch
112129
// can't be hit. Branching to an unreachable instruction
113130
// lets LLVM know this
114-
_ => (None, self.unreachable_block())
131+
_ => (None, self.unreachable_block().llbb)
115132
};
116-
let switch = bcx.switch(discr, default_blk.llbb, targets.len());
133+
let switch = bcx.switch(discr, default_blk, targets.len());
117134
assert_eq!(adt_def.variants.len(), targets.len());
118135
for (adt_variant, &target) in adt_def.variants.iter().zip(targets) {
119136
if default_bb != Some(target) {
120-
let llbb = self.llblock(target);
137+
let llbb = llblock(self, target);
121138
let llval = bcx.with_block(|bcx| adt::trans_case(
122139
bcx, &repr, Disr::from(adt_variant.disr_val)));
123140
build::AddCase(switch, llval, llbb)
@@ -129,10 +146,10 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
129146
let (otherwise, targets) = targets.split_last().unwrap();
130147
let discr = bcx.load(self.trans_lvalue(&bcx, discr).llval);
131148
let discr = bcx.with_block(|bcx| base::to_immediate(bcx, discr, switch_ty));
132-
let switch = bcx.switch(discr, self.llblock(*otherwise), values.len());
149+
let switch = bcx.switch(discr, llblock(self, *otherwise), values.len());
133150
for (value, target) in values.iter().zip(targets) {
134151
let val = Const::from_constval(bcx.ccx(), value.clone(), switch_ty);
135-
let llbb = self.llblock(*target);
152+
let llbb = llblock(self, *target);
136153
build::AddCase(switch, val.llval, llbb)
137154
}
138155
}
@@ -148,7 +165,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
148165
let ty = lvalue.ty.to_ty(bcx.tcx());
149166
// Double check for necessity to drop
150167
if !glue::type_needs_drop(bcx.tcx(), ty) {
151-
funclet_br(bcx, self.llblock(target));
168+
funclet_br(self, bcx, target);
152169
return;
153170
}
154171
let drop_fn = glue::get_drop_glue(bcx.ccx(), ty);
@@ -163,15 +180,12 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
163180
let unwind = self.make_landing_pad(uwbcx);
164181
bcx.invoke(drop_fn,
165182
&[llvalue],
166-
self.llblock(target),
183+
self.blocks[target.index()].llbb,
167184
unwind.llbb(),
168185
cleanup_bundle.as_ref());
169-
self.bcx(target).at_start(|bcx| {
170-
debug_loc.apply_to_bcx(bcx);
171-
});
172186
} else {
173187
bcx.call(drop_fn, &[llvalue], cleanup_bundle.as_ref());
174-
funclet_br(bcx, self.llblock(target));
188+
funclet_br(self, bcx, target);
175189
}
176190
}
177191

@@ -213,7 +227,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
213227
let llptr = self.trans_operand(&bcx, &args[0]).immediate();
214228
let val = self.trans_operand(&bcx, &args[1]);
215229
self.store_operand(&bcx, llptr, val);
216-
funclet_br(bcx, self.llblock(target));
230+
funclet_br(self, bcx, target);
217231
return;
218232
}
219233

@@ -223,7 +237,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
223237
this.trans_transmute(&bcx, &args[0], dest);
224238
});
225239

226-
funclet_br(bcx, self.llblock(target));
240+
funclet_br(self, bcx, target);
227241
return;
228242
}
229243

@@ -328,7 +342,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
328342
}
329343

330344
if let Some((_, target)) = *destination {
331-
funclet_br(bcx, self.llblock(target));
345+
funclet_br(self, bcx, target);
332346
} else {
333347
// trans_intrinsic_call already used Unreachable.
334348
// bcx.unreachable();
@@ -376,9 +390,8 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
376390
ty: sig.output.unwrap()
377391
};
378392
self.store_return(&bcx, ret_dest, fn_ty.ret, op);
379-
funclet_br(bcx, self.llblock(target));
393+
funclet_br(self, bcx, target);
380394
} else {
381-
// no need to drop args, because the call never returns
382395
bcx.unreachable();
383396
}
384397
}
@@ -581,10 +594,6 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
581594
self.blocks[bb.index()].build()
582595
}
583596

584-
pub fn llblock(&self, bb: mir::BasicBlock) -> BasicBlockRef {
585-
self.blocks[bb.index()].llbb
586-
}
587-
588597
fn make_return_dest(&mut self, bcx: &BlockAndBuilder<'bcx, 'tcx>,
589598
dest: &mir::Lvalue<'tcx>, fn_ret_ty: &ArgType,
590599
llargs: &mut Vec<ValueRef>, is_intrinsic: bool) -> ReturnDest {

src/librustc_trans/mir/mod.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -228,8 +228,7 @@ pub fn trans_mir<'blk, 'tcx: 'blk>(fcx: &'blk FunctionContext<'blk, 'tcx>) {
228228
let block = BasicBlock(block.llbb);
229229
// Unreachable block
230230
if !visited.contains(bb.index()) {
231-
block.delete();
232-
} else if block.pred_iter().count() == 0 {
231+
debug!("trans_mir: block {:?} was not visited", bb);
233232
block.delete();
234233
}
235234
}

src/test/run-fail/issue-30380.rs

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

11+
#![feature(rustc_attrs)]
12+
1113
// check that panics in destructors during assignment do not leave
1214
// destroyed values lying around for other destructors to observe.
1315

@@ -33,6 +35,7 @@ impl<'a> Drop for Observer<'a> {
3335
}
3436
}
3537

38+
#[rustc_mir]
3639
fn foo(b: &mut Observer) {
3740
*b.0 = FilledOnDrop(1);
3841
}

src/test/run-pass/dynamic-drop.rs

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

11-
use std::cell::RefCell;
11+
#![feature(rustc_attrs)]
12+
13+
use std::cell::{Cell, RefCell};
14+
use std::panic;
15+
use std::usize;
16+
17+
struct InjectedFailure;
1218

1319
struct Allocator {
1420
data: RefCell<Vec<bool>>,
21+
failing_op: usize,
22+
cur_ops: Cell<usize>,
1523
}
1624

25+
impl panic::UnwindSafe for Allocator {}
26+
impl panic::RefUnwindSafe for Allocator {}
27+
1728
impl Drop for Allocator {
1829
fn drop(&mut self) {
1930
let data = self.data.borrow();
@@ -24,8 +35,20 @@ impl Drop for Allocator {
2435
}
2536

2637
impl Allocator {
27-
fn new() -> Self { Allocator { data: RefCell::new(vec![]) } }
38+
fn new(failing_op: usize) -> Self {
39+
Allocator {
40+
failing_op: failing_op,
41+
cur_ops: Cell::new(0),
42+
data: RefCell::new(vec![])
43+
}
44+
}
2845
fn alloc(&self) -> Ptr {
46+
self.cur_ops.set(self.cur_ops.get() + 1);
47+
48+
if self.cur_ops.get() == self.failing_op {
49+
panic!(InjectedFailure);
50+
}
51+
2952
let mut data = self.data.borrow_mut();
3053
let addr = data.len();
3154
data.push(true);
@@ -42,25 +65,34 @@ impl<'a> Drop for Ptr<'a> {
4265
}
4366
ref mut d => *d = false
4467
}
68+
69+
self.1.cur_ops.set(self.1.cur_ops.get()+1);
70+
71+
if self.1.cur_ops.get() == self.1.failing_op {
72+
panic!(InjectedFailure);
73+
}
4574
}
4675
}
4776

77+
#[rustc_mir]
4878
fn dynamic_init(a: &Allocator, c: bool) {
4979
let _x;
5080
if c {
5181
_x = Some(a.alloc());
5282
}
5383
}
5484

55-
fn dynamic_drop(a: &Allocator, c: bool) -> Option<Ptr> {
85+
#[rustc_mir]
86+
fn dynamic_drop(a: &Allocator, c: bool) {
5687
let x = a.alloc();
5788
if c {
5889
Some(x)
5990
} else {
6091
None
61-
}
92+
};
6293
}
6394

95+
#[rustc_mir]
6496
fn assignment2(a: &Allocator, c0: bool, c1: bool) {
6597
let mut _v = a.alloc();
6698
let mut _w = a.alloc();
@@ -73,6 +105,7 @@ fn assignment2(a: &Allocator, c0: bool, c1: bool) {
73105
}
74106
}
75107

108+
#[rustc_mir]
76109
fn assignment1(a: &Allocator, c0: bool) {
77110
let mut _v = a.alloc();
78111
let mut _w = a.alloc();
@@ -82,19 +115,42 @@ fn assignment1(a: &Allocator, c0: bool) {
82115
_v = _w;
83116
}
84117

118+
fn run_test<F>(mut f: F)
119+
where F: FnMut(&Allocator)
120+
{
121+
let first_alloc = Allocator::new(usize::MAX);
122+
f(&first_alloc);
123+
124+
for failing_op in 1..first_alloc.cur_ops.get()+1 {
125+
let alloc = Allocator::new(failing_op);
126+
let alloc = &alloc;
127+
let f = panic::AssertUnwindSafe(&mut f);
128+
let result = panic::catch_unwind(move || {
129+
f.0(alloc);
130+
});
131+
match result {
132+
Ok(..) => panic!("test executed {} ops but now {}",
133+
first_alloc.cur_ops.get(), alloc.cur_ops.get()),
134+
Err(e) => {
135+
if e.downcast_ref::<InjectedFailure>().is_none() {
136+
panic::resume_unwind(e);
137+
}
138+
}
139+
}
140+
}
141+
}
85142

86143
fn main() {
87-
let a = Allocator::new();
88-
dynamic_init(&a, false);
89-
dynamic_init(&a, true);
90-
dynamic_drop(&a, false);
91-
dynamic_drop(&a, true);
92-
93-
assignment2(&a, false, false);
94-
assignment2(&a, false, true);
95-
assignment2(&a, true, false);
96-
assignment2(&a, true, true);
97-
98-
assignment1(&a, false);
99-
assignment1(&a, true);
144+
run_test(|a| dynamic_init(a, false));
145+
run_test(|a| dynamic_init(a, true));
146+
run_test(|a| dynamic_drop(a, false));
147+
run_test(|a| dynamic_drop(a, true));
148+
149+
run_test(|a| assignment2(a, false, false));
150+
run_test(|a| assignment2(a, false, true));
151+
run_test(|a| assignment2(a, true, false));
152+
run_test(|a| assignment2(a, true, true));
153+
154+
run_test(|a| assignment1(a, false));
155+
run_test(|a| assignment1(a, true));
100156
}

0 commit comments

Comments
 (0)