]> git.lizzy.rs Git - rust.git/commitdiff
add an #[used] attribute
authorJorge Aparicio <japaricious@gmail.com>
Mon, 20 Feb 2017 19:42:47 +0000 (14:42 -0500)
committerJorge Aparicio <japaricious@gmail.com>
Wed, 5 Apr 2017 18:40:11 +0000 (13:40 -0500)
similar to GCC's __attribute((used))__. This attribute prevents LLVM from
optimizing away a non-exported symbol, within a compilation unit (object file),
when there are no references to it.

This is better explained with an example:

```
#[used]
static LIVE: i32 = 0;

static REFERENCED: i32 = 0;

static DEAD: i32 = 0;

fn internal() {}

pub fn exported() -> &'static i32 {
    &REFERENCED
}
```

Without optimizations, LLVM pretty much preserves all the static variables and
functions within the compilation unit.

```
$ rustc --crate-type=lib --emit=obj symbols.rs && nm -C symbols.o
0000000000000000 t drop::h1be0f8f27a2ba94a
0000000000000000 r symbols::REFERENCED::hb3bdfd46050bc84c
0000000000000000 r symbols::DEAD::hc2ea8f9bd06f380b
0000000000000000 r symbols::LIVE::h0970cf9889edb56e
0000000000000000 T symbols::exported::h6f096c2b1fc292b2
0000000000000000 t symbols::internal::h0ac1aadbc1e3a494
```

With optimizations, LLVM will drop dead code. Here `internal` is dropped because
it's not a exported function/symbol (i.e. not `pub`lic). `DEAD` is dropped for
the same reason. `REFERENCED` is preserved, even though it's not exported,
because it's referenced by the `exported` function. Finally, `LIVE` survives
because of the `#[used]` attribute even though it's not exported or referenced.

```
$ rustc --crate-type=lib -C opt-level=3 --emit=obj symbols.rs && nm -C symbols.o
0000000000000000 r symbols::REFERENCED::hb3bdfd46050bc84c
0000000000000000 r symbols::LIVE::h0970cf9889edb56e
0000000000000000 T symbols::exported::h6f096c2b1fc292b2
```

Note that the linker knows nothing about `#[used]` and will drop `LIVE`
because no other object references to it.

```
$ echo 'fn main() {}' >> symbols.rs
$ rustc symbols.rs && nm -C symbols | grep LIVE
```

At this time, `#[used]` only works on `static` variables.

src/librustc_trans/base.rs
src/librustc_trans/consts.rs
src/librustc_trans/context.rs
src/libsyntax/feature_gate.rs

index ec45c5593632ed3fddbc5bf18924ffdec73fc740..63258b74533179cb0cf63ec9e200ed38df183070 100644 (file)
@@ -50,7 +50,7 @@
 use callee;
 use common::{C_bool, C_bytes_in_context, C_i32, C_uint};
 use collector::{self, TransItemCollectionMode};
-use common::{C_struct_in_context, C_u64, C_undef};
+use common::{C_struct_in_context, C_u64, C_undef, C_array};
 use common::CrateContext;
 use common::{type_is_zero_size, val_ty};
 use common;
@@ -1187,6 +1187,24 @@ fn trans_def_task<'a, 'tcx>(ccx: CrateContext<'a, 'tcx>,
                 }
             }
 
+            // Create llvm.used variable
+            if !ccx.used_statics().borrow().is_empty() {
+                debug!("llvm.used");
+
+                let name = CString::new("llvm.used").unwrap();
+                let section = CString::new("llvm.metadata").unwrap();
+                let array = C_array(Type::i8(&ccx).ptr_to(), &*ccx.used_statics().borrow());
+
+                unsafe {
+                    let g = llvm::LLVMAddGlobal(ccx.llmod(),
+                                                val_ty(array).to_ref(),
+                                                name.as_ptr());
+                    llvm::LLVMSetInitializer(g, array);
+                    llvm::LLVMRustSetLinkage(g, llvm::Linkage::AppendingLinkage);
+                    llvm::LLVMSetSection(g, section.as_ptr());
+                }
+            }
+
             // Finalize debuginfo
             if ccx.sess().opts.debuginfo != NoDebugInfo {
                 debuginfo::finalize(&ccx);
index 0c3d211912add2498885a63f8b2cda61c15ba02e..9974155f7c07d4d4eb8880303f1562f5a53481af 100644 (file)
@@ -276,6 +276,10 @@ pub fn trans_static<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
 
         base::set_link_section(ccx, g, attrs);
 
+        if attr::contains_name(attrs, "used") {
+            ccx.used_statics().borrow_mut().push(g);
+        }
+
         Ok(g)
     }
 }
index 73602dc420b3f9999be6534e41e5df674004cc91..2eca0a18e2b380520bcf30ae5097e240e1f2aba3 100644 (file)
@@ -132,6 +132,8 @@ pub struct LocalCrateContext<'tcx> {
     /// to constants.)
     statics_to_rauw: RefCell<Vec<(ValueRef, ValueRef)>>,
 
+    used_statics: RefCell<Vec<ValueRef>>,
+
     lltypes: RefCell<FxHashMap<Ty<'tcx>, Type>>,
     llsizingtypes: RefCell<FxHashMap<Ty<'tcx>, Type>>,
     type_hashcodes: RefCell<FxHashMap<Ty<'tcx>, String>>,
@@ -587,6 +589,7 @@ fn new<'a>(shared: &SharedCrateContext<'a, 'tcx>,
                 impl_method_cache: RefCell::new(FxHashMap()),
                 closure_bare_wrapper_cache: RefCell::new(FxHashMap()),
                 statics_to_rauw: RefCell::new(Vec::new()),
+                used_statics: RefCell::new(Vec::new()),
                 lltypes: RefCell::new(FxHashMap()),
                 llsizingtypes: RefCell::new(FxHashMap()),
                 type_hashcodes: RefCell::new(FxHashMap()),
@@ -754,6 +757,10 @@ pub fn statics_to_rauw<'a>(&'a self) -> &'a RefCell<Vec<(ValueRef, ValueRef)>> {
         &self.local().statics_to_rauw
     }
 
+    pub fn used_statics<'a>(&'a self) -> &'a RefCell<Vec<ValueRef>> {
+        &self.local().used_statics
+    }
+
     pub fn lltypes<'a>(&'a self) -> &'a RefCell<FxHashMap<Ty<'tcx>, Type>> {
         &self.local().lltypes
     }
index 12d25ca4274fea1fe09ef93cbcd49784b407b4ec..66a813025c4376bebeda76bcef2a594bf3cc4a21 100644 (file)
@@ -337,11 +337,15 @@ pub fn new() -> Features {
     // `extern "x86-interrupt" fn()`
     (active, abi_x86_interrupt, "1.17.0", Some(40180)),
 
+
     // Allows the `catch {...}` expression
     (active, catch_expr, "1.17.0", Some(31436)),
 
     // See rust-lang/rfcs#1414. Allows code like `let x: &'static u32 = &42` to work.
     (active, rvalue_static_promotion, "1.15.1", Some(38865)),
+
+    // Used to preserve symbols
+    (active, used, "1.18.0", None),
 );
 
 declare_features! (
@@ -748,6 +752,10 @@ pub fn is_builtin_attr(attr: &ast::Attribute) -> bool {
                                   "unwind_attributes",
                                   "#[unwind] is experimental",
                                   cfg_fn!(unwind_attributes))),
+    ("used", Whitelisted, Gated(
+        Stability::Unstable, "used",
+        "the `#[used]` attribute is an experimental feature",
+        cfg_fn!(used))),
 
     // used in resolve
     ("prelude_import", Whitelisted, Gated(Stability::Unstable,