@@ -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