@@ -218,63 +218,74 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
218
218
let fn_ptr = callee. reify ( bcx. ccx ( ) ) . val ;
219
219
220
220
// 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 ( ) ;
246
267
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 ) ;
252
269
}
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) ) ;
265
274
}
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 {
269
279
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 ) ;
273
281
}
274
282
for op in args {
275
283
self . set_operand_dropped ( & bcx, op) ;
276
284
}
277
285
funclet_br ( bcx, self . llblock ( target) ) ;
286
+ } else {
287
+ // no need to drop args, because the call never returns
288
+ bcx. unreachable ( ) ;
278
289
}
279
290
}
280
291
}
0 commit comments