Skip to content

Commit ee7687a

Browse files
committed
mir: Reintroduce the temporary block after invokes, to handle critical edges.
1 parent 41fc5f7 commit ee7687a

File tree

1 file changed

+59
-48
lines changed

1 file changed

+59
-48
lines changed

src/librustc_trans/trans/mir/block.rs

Lines changed: 59 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -218,63 +218,74 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
218218
let fn_ptr = callee.reify(bcx.ccx()).val;
219219

220220
// Many different ways to call a function handled here
221-
match (cleanup, destination) {
222-
// The two cases below are the only ones to use LLVM’s `invoke`.
223-
(&Some(cleanup), &None) => {
224-
let cleanup = self.bcx(cleanup);
225-
let landingpad = self.make_landing_pad(cleanup);
226-
let unreachable_blk = self.unreachable_block();
227-
let cs = bcx.invoke(fn_ptr,
228-
&llargs,
229-
unreachable_blk.llbb,
230-
landingpad.llbb(),
231-
cleanup_bundle.as_ref());
232-
fn_ty.apply_attrs_callsite(cs);
233-
landingpad.at_start(|bcx| for op in args {
234-
self.set_operand_dropped(bcx, op);
235-
});
236-
},
237-
(&Some(cleanup), &Some((_, success))) => {
238-
let cleanup = self.bcx(cleanup);
239-
let landingpad = self.make_landing_pad(cleanup);
240-
let invokeret = bcx.invoke(fn_ptr,
241-
&llargs,
242-
self.llblock(success),
243-
landingpad.llbb(),
244-
cleanup_bundle.as_ref());
245-
fn_ty.apply_attrs_callsite(invokeret);
221+
if let Some(cleanup) = cleanup.map(|bb| self.bcx(bb)) {
222+
// We translate the copy into a temporary block. The temporary block is
223+
// necessary because the current block has already been terminated (by
224+
// `invoke`) and we cannot really translate into the target block
225+
// because:
226+
// * The target block may have more than a single precedesor;
227+
// * Some LLVM insns cannot have a preceeding store insn (phi,
228+
// cleanuppad), and adding/prepending the store now may render
229+
// those other instructions invalid.
230+
//
231+
// NB: This approach still may break some LLVM code. For example if the
232+
// target block starts with a `phi` (which may only match on immediate
233+
// precedesors), it cannot know about this temporary block thus
234+
// resulting in an invalid code:
235+
//
236+
// this:
237+
// …
238+
// %0 = …
239+
// %1 = invoke to label %temp …
240+
// temp:
241+
// store ty %1, ty* %dest
242+
// br label %actualtargetblock
243+
// actualtargetblock: ; preds: %temp, …
244+
// phi … [%this, …], [%0, …] ; ERROR: phi requires to match only on
245+
// ; immediate precedesors
246+
247+
let ret_bcx = if destination.is_some() {
248+
self.fcx.new_block("", None)
249+
} else {
250+
self.unreachable_block()
251+
};
252+
let landingpad = self.make_landing_pad(cleanup);
253+
254+
let invokeret = bcx.invoke(fn_ptr,
255+
&llargs,
256+
ret_bcx.llbb,
257+
landingpad.llbb(),
258+
cleanup_bundle.as_ref());
259+
fn_ty.apply_attrs_callsite(invokeret);
260+
261+
landingpad.at_start(|bcx| for op in args {
262+
self.set_operand_dropped(bcx, op);
263+
});
264+
265+
if let Some((_, target)) = *destination {
266+
let ret_bcx = ret_bcx.build();
246267
if let Some(ret_dest) = ret_dest {
247-
// We translate the copy straight into the beginning of the target
248-
// block.
249-
self.bcx(success).at_start(|bcx| bcx.with_block( |bcx| {
250-
fn_ty.ret.store(bcx, invokeret, ret_dest.llval);
251-
}));
268+
fn_ty.ret.store(&ret_bcx, invokeret, ret_dest.llval);
252269
}
253-
self.bcx(success).at_start(|bcx| for op in args {
254-
self.set_operand_dropped(bcx, op);
255-
});
256-
landingpad.at_start(|bcx| for op in args {
257-
self.set_operand_dropped(bcx, op);
258-
});
259-
},
260-
(&None, &None) => {
261-
let cs = bcx.call(fn_ptr, &llargs, cleanup_bundle.as_ref());
262-
fn_ty.apply_attrs_callsite(cs);
263-
// no need to drop args, because the call never returns
264-
bcx.unreachable();
270+
for op in args {
271+
self.set_operand_dropped(&ret_bcx, op);
272+
}
273+
ret_bcx.br(self.llblock(target));
265274
}
266-
(&None, &Some((_, target))) => {
267-
let llret = bcx.call(fn_ptr, &llargs, cleanup_bundle.as_ref());
268-
fn_ty.apply_attrs_callsite(llret);
275+
} else {
276+
let llret = bcx.call(fn_ptr, &llargs, cleanup_bundle.as_ref());
277+
fn_ty.apply_attrs_callsite(llret);
278+
if let Some((_, target)) = *destination {
269279
if let Some(ret_dest) = ret_dest {
270-
bcx.with_block(|bcx| {
271-
fn_ty.ret.store(bcx, llret, ret_dest.llval);
272-
});
280+
fn_ty.ret.store(&bcx, llret, ret_dest.llval);
273281
}
274282
for op in args {
275283
self.set_operand_dropped(&bcx, op);
276284
}
277285
funclet_br(bcx, self.llblock(target));
286+
} else {
287+
// no need to drop args, because the call never returns
288+
bcx.unreachable();
278289
}
279290
}
280291
}

0 commit comments

Comments
 (0)