@@ -561,6 +561,55 @@ impl<'a, 'tcx> FunctionContext<'a, 'tcx> {
561561 }
562562 }
563563 }
564+
565+ /// By default, LLVM lowers `resume` instructions into calls to `_Unwind_Resume`
566+ /// defined in libgcc, however, unlike personality routines, there is no easy way to
567+ /// override that symbol. This method injects a local-scoped `_Unwind_Resume` function
568+ /// which immediately defers to the user-defined `eh_unwind_resume` lang item.
569+ pub fn inject_unwind_resume_hook ( & self ) {
570+ let ccx = self . ccx ;
571+ if !ccx. sess ( ) . target . target . options . custom_unwind_resume ||
572+ ccx. unwind_resume_hooked ( ) . get ( ) {
573+ return ;
574+ }
575+
576+ let new_resume = match ccx. tcx ( ) . lang_items . eh_unwind_resume ( ) {
577+ Some ( did) => callee:: trans_fn_ref ( ccx, did, ExprId ( 0 ) , & self . param_substs ) . val ,
578+ None => {
579+ let fty = Type :: variadic_func ( & [ ] , & Type :: void ( self . ccx ) ) ;
580+ declare:: declare_cfn ( self . ccx , "rust_eh_unwind_resume" , fty,
581+ self . ccx . tcx ( ) . mk_nil ( ) )
582+ }
583+ } ;
584+
585+ unsafe {
586+ let resume_type = Type :: func ( & [ Type :: i8 ( ccx) . ptr_to ( ) ] , & Type :: void ( ccx) ) ;
587+ let old_resume = llvm:: LLVMAddFunction ( ccx. llmod ( ) ,
588+ "_Unwind_Resume\0 " . as_ptr ( ) as * const _ ,
589+ resume_type. to_ref ( ) ) ;
590+ llvm:: SetLinkage ( old_resume, llvm:: InternalLinkage ) ;
591+ let llbb = llvm:: LLVMAppendBasicBlockInContext ( ccx. llcx ( ) ,
592+ old_resume,
593+ "\0 " . as_ptr ( ) as * const _ ) ;
594+ let builder = ccx. builder ( ) ;
595+ builder. position_at_end ( llbb) ;
596+ builder. call ( new_resume, & [ llvm:: LLVMGetFirstParam ( old_resume) ] , None ) ;
597+ builder. unreachable ( ) ; // it should never return
598+
599+ // Until DwarfEHPrepare pass has run, _Unwind_Resume is not referenced by any live code
600+ // and is subject to dead code elimination. Here we add _Unwind_Resume to @llvm.globals
601+ // to prevent that.
602+ let i8p_ty = Type :: i8p ( ccx) ;
603+ let used_ty = Type :: array ( & i8p_ty, 1 ) ;
604+ let used = llvm:: LLVMAddGlobal ( ccx. llmod ( ) , used_ty. to_ref ( ) ,
605+ "llvm.used\0 " . as_ptr ( ) as * const _ ) ;
606+ let old_resume = llvm:: LLVMConstBitCast ( old_resume, i8p_ty. to_ref ( ) ) ;
607+ llvm:: LLVMSetInitializer ( used, C_array ( i8p_ty, & [ old_resume] ) ) ;
608+ llvm:: SetLinkage ( used, llvm:: AppendingLinkage ) ;
609+ llvm:: LLVMSetSection ( used, "llvm.metadata\0 " . as_ptr ( ) as * const _ )
610+ }
611+ ccx. unwind_resume_hooked ( ) . set ( true ) ;
612+ }
564613}
565614
566615// Basic block context. We create a block context for each basic block
0 commit comments