add an #[used] attribute
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.
This commit is contained in:
parent
2564711e80
commit
4c7e277340
@ -50,7 +50,7 @@ use builder::Builder;
|
||||
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 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, '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);
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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 @@ impl<'tcx> LocalCrateContext<'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 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> {
|
||||
&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
|
||||
}
|
||||
|
@ -337,11 +337,15 @@ declare_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 const BUILTIN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeG
|
||||
"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,
|
||||
|
Loading…
Reference in New Issue
Block a user