]> git.lizzy.rs Git - rust.git/commitdiff
Revisit implementation of custom unwind resume;
authorVadim Chugunov <vadimcn@gmail.com>
Sun, 18 Oct 2015 21:17:34 +0000 (14:17 -0700)
committerVadim Chugunov <vadimcn@gmail.com>
Mon, 19 Oct 2015 02:57:28 +0000 (19:57 -0700)
Rather than injecting a local `_Unwind_Resume` into the current translation unit,
just replace `resume` instruction with a direct call the the `eh_unwind_resume` lang item.
This is likely to be more robust in the face of future LLVM changes, and also allows us to delegate
work back to libgcc's `_Unwind_Resume`.

src/librustc_trans/trans/base.rs
src/librustc_trans/trans/cleanup.rs
src/librustc_trans/trans/common.rs
src/librustc_trans/trans/context.rs
src/librustc_trans/trans/intrinsic.rs

index 9fa1aaf76f819dc5fcd1d31c42c954a9ff874e69..b37a0f6318c20e1f1262ca985aabab36d0f1c194 100644 (file)
@@ -944,6 +944,19 @@ pub fn call_lifetime_end(cx: Block, ptr: ValueRef) {
     Call(cx, lifetime_end, &[C_u64(ccx, size), ptr], None, DebugLoc::None);
 }
 
+// Generates code for resumption of unwind at the end of a landing pad.
+pub fn trans_unwind_resume(bcx: Block, lpval: ValueRef) {
+    if !bcx.sess().target.target.options.custom_unwind_resume {
+        Resume(bcx, lpval);
+    } else {
+        let exc_ptr = ExtractValue(bcx, lpval, 0);
+        let llunwresume = bcx.fcx.eh_unwind_resume();
+        Call(bcx, llunwresume, &[exc_ptr], None, DebugLoc::None);
+        Unreachable(bcx);
+    }
+}
+
+
 pub fn call_memcpy(cx: Block, dst: ValueRef, src: ValueRef, n_bytes: ValueRef, align: u32) {
     let _icx = push_ctxt("call_memcpy");
     let ccx = cx.ccx();
index d226bc3f155df10c8b33e18bcbe3a86bffa9422e..ffdc2701f8158fc189a9ab3fff4ef709cb87b396 100644 (file)
@@ -732,7 +732,7 @@ fn trans_cleanups_to_exit_scope(&'blk self,
                             "create_landing_pad() should have set this");
                         let lp = build::Load(prev_bcx, personality);
                         base::call_lifetime_end(prev_bcx, personality);
-                        build::Resume(prev_bcx, lp);
+                        base::trans_unwind_resume(prev_bcx, lp);
                         prev_llbb = prev_bcx.llbb;
                         break;
                     }
@@ -845,8 +845,6 @@ fn get_or_create_landing_pad(&'blk self) -> BasicBlockRef {
 
         debug!("get_or_create_landing_pad");
 
-        self.inject_unwind_resume_hook();
-
         // Check if a landing pad block exists; if not, create one.
         {
             let mut scopes = self.scopes.borrow_mut();
index d160465c619b95e80f6c2e907b6e33c987d87fe0..b39b7818a6350f7d66d521c46a44598168cb75a5 100644 (file)
@@ -561,53 +561,33 @@ pub fn eh_personality(&self) -> ValueRef {
         }
     }
 
-    /// By default, LLVM lowers `resume` instructions into calls to `_Unwind_Resume`
-    /// defined in libgcc, however, unlike personality routines, there is no easy way to
-    /// override that symbol.  This method injects a local-scoped `_Unwind_Resume` function
-    /// which immediately defers to the user-defined `eh_unwind_resume` lang item.
-    pub fn inject_unwind_resume_hook(&self) {
-        let ccx = self.ccx;
-        if !ccx.sess().target.target.options.custom_unwind_resume ||
-           ccx.unwind_resume_hooked().get() {
-            return;
-        }
-
-        let new_resume = match ccx.tcx().lang_items.eh_unwind_resume() {
-            Some(did) => callee::trans_fn_ref(ccx, did, ExprId(0), &self.param_substs).val,
+    // Returns a ValueRef of the "eh_unwind_resume" lang item if one is defined,
+    // otherwise declares it as an external funtion.
+    pub fn eh_unwind_resume(&self) -> ValueRef {
+        use trans::attributes;
+        assert!(self.ccx.sess().target.target.options.custom_unwind_resume);
+        match self.ccx.tcx().lang_items.eh_unwind_resume() {
+            Some(def_id) => {
+                callee::trans_fn_ref(self.ccx, def_id, ExprId(0),
+                                     self.param_substs).val
+            }
             None => {
-                let fty = Type::variadic_func(&[], &Type::void(self.ccx));
-                declare::declare_cfn(self.ccx, "rust_eh_unwind_resume", fty,
-                                     self.ccx.tcx().mk_nil())
+                let mut unwresume = self.ccx.eh_unwind_resume().borrow_mut();
+                match *unwresume {
+                    Some(llfn) => llfn,
+                    None => {
+                        let fty = Type::func(&[Type::i8p(self.ccx)], &Type::void(self.ccx));
+                        let llfn = declare::declare_fn(self.ccx,
+                                                       "rust_eh_unwind_resume",
+                                                       llvm::CCallConv,
+                                                       fty, ty::FnDiverging);
+                        attributes::unwind(llfn, true);
+                        *unwresume = Some(llfn);
+                        llfn
+                    }
+                }
             }
-        };
-
-        unsafe {
-            let resume_type = Type::func(&[Type::i8(ccx).ptr_to()], &Type::void(ccx));
-            let old_resume = llvm::LLVMAddFunction(ccx.llmod(),
-                                                   "_Unwind_Resume\0".as_ptr() as *const _,
-                                                   resume_type.to_ref());
-            llvm::SetLinkage(old_resume, llvm::InternalLinkage);
-            let llbb = llvm::LLVMAppendBasicBlockInContext(ccx.llcx(),
-                                                           old_resume,
-                                                           "\0".as_ptr() as *const _);
-            let builder = ccx.builder();
-            builder.position_at_end(llbb);
-            builder.call(new_resume, &[llvm::LLVMGetFirstParam(old_resume)], None);
-            builder.unreachable(); // it should never return
-
-            // Until DwarfEHPrepare pass has run, _Unwind_Resume is not referenced by any live code
-            // and is subject to dead code elimination.  Here we add _Unwind_Resume to @llvm.globals
-            // to prevent that.
-            let i8p_ty = Type::i8p(ccx);
-            let used_ty = Type::array(&i8p_ty, 1);
-            let used = llvm::LLVMAddGlobal(ccx.llmod(), used_ty.to_ref(),
-                                           "llvm.used\0".as_ptr() as *const _);
-            let old_resume = llvm::LLVMConstBitCast(old_resume, i8p_ty.to_ref());
-            llvm::LLVMSetInitializer(used, C_array(i8p_ty, &[old_resume]));
-            llvm::SetLinkage(used, llvm::AppendingLinkage);
-            llvm::LLVMSetSection(used, "llvm.metadata\0".as_ptr() as *const _)
         }
-        ccx.unwind_resume_hooked().set(true);
     }
 }
 
index 94ce92ab972f492264cd6d3b5701bfde33b56af9..0f7ea334c889d4ea53815757112c9a0e750c1981 100644 (file)
@@ -146,8 +146,8 @@ pub struct LocalCrateContext<'tcx> {
     dbg_cx: Option<debuginfo::CrateDebugContext<'tcx>>,
 
     eh_personality: RefCell<Option<ValueRef>>,
+    eh_unwind_resume: RefCell<Option<ValueRef>>,
     rust_try_fn: RefCell<Option<ValueRef>>,
-    unwind_resume_hooked: Cell<bool>,
 
     intrinsics: RefCell<FnvHashMap<&'static str, ValueRef>>,
 
@@ -466,8 +466,8 @@ fn new<'a>(shared: &SharedCrateContext<'a, 'tcx>,
                 closure_vals: RefCell::new(FnvHashMap()),
                 dbg_cx: dbg_cx,
                 eh_personality: RefCell::new(None),
+                eh_unwind_resume: RefCell::new(None),
                 rust_try_fn: RefCell::new(None),
-                unwind_resume_hooked: Cell::new(false),
                 intrinsics: RefCell::new(FnvHashMap()),
                 n_llvm_insns: Cell::new(0),
                 trait_cache: RefCell::new(FnvHashMap()),
@@ -728,12 +728,12 @@ pub fn eh_personality<'a>(&'a self) -> &'a RefCell<Option<ValueRef>> {
         &self.local.eh_personality
     }
 
-    pub fn rust_try_fn<'a>(&'a self) -> &'a RefCell<Option<ValueRef>> {
-        &self.local.rust_try_fn
+    pub fn eh_unwind_resume<'a>(&'a self) -> &'a RefCell<Option<ValueRef>> {
+        &self.local.eh_unwind_resume
     }
 
-    pub fn unwind_resume_hooked<'a>(&'a self) -> &'a Cell<bool> {
-        &self.local.unwind_resume_hooked
+    pub fn rust_try_fn<'a>(&'a self) -> &'a RefCell<Option<ValueRef>> {
+        &self.local.rust_try_fn
     }
 
     fn intrinsics<'a>(&'a self) -> &'a RefCell<FnvHashMap<&'static str, ValueRef>> {
index 5e781bdf3306ac8a2e3a12da5f550e12697ab5db..b8fa826b93948be02d44c14252b27701629f995d 100644 (file)
@@ -1315,7 +1315,7 @@ fn trans_msvc_try<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
         // The "catch-resume" block is where we're running this landing pad but
         // we actually need to not catch the exception, so just resume the
         // exception to return.
-        Resume(catch_resume, vals);
+        trans_unwind_resume(catch_resume, vals);
 
         // On the successful branch we just return null.
         Ret(then, C_null(Type::i8p(ccx)), dloc);