From 3055f419c7c61d90be69fbcd4770b48eaff03213 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Mon, 20 Feb 2017 14:42:47 -0500 Subject: [PATCH 1/3] 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. --- src/librustc_trans/base.rs | 20 +++++++++++++++++++- src/librustc_trans/consts.rs | 4 ++++ src/librustc_trans/context.rs | 7 +++++++ src/libsyntax/feature_gate.rs | 7 +++++++ 4 files changed, 37 insertions(+), 1 deletion(-) diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs index 36f6fa764390..af9c6bca671d 100644 --- a/src/librustc_trans/base.rs +++ b/src/librustc_trans/base.rs @@ -55,7 +55,7 @@ use builder::Builder; use callee::{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::{fulfill_obligation}; use common::{type_is_zero_size, val_ty}; @@ -1243,6 +1243,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); diff --git a/src/librustc_trans/consts.rs b/src/librustc_trans/consts.rs index bf1d9886ae7f..cc3ddeef5394 100644 --- a/src/librustc_trans/consts.rs +++ b/src/librustc_trans/consts.rs @@ -275,6 +275,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) } } diff --git a/src/librustc_trans/context.rs b/src/librustc_trans/context.rs index d5f7549ece07..77c80d2a2361 100644 --- a/src/librustc_trans/context.rs +++ b/src/librustc_trans/context.rs @@ -136,6 +136,8 @@ pub struct LocalCrateContext<'tcx> { /// to constants.) statics_to_rauw: RefCell>, + used_statics: RefCell>, + lltypes: RefCell, Type>>, llsizingtypes: RefCell, Type>>, type_hashcodes: RefCell, String>>, @@ -606,6 +608,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()), @@ -786,6 +789,10 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> { &self.local().statics_to_rauw } + pub fn used_statics<'a>(&'a self) -> &'a RefCell> { + &self.local().used_statics + } + pub fn lltypes<'a>(&'a self) -> &'a RefCell, Type>> { &self.local().lltypes } diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 6eb7d449f269..cafb622ea469 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -340,6 +340,9 @@ declare_features! ( // `extern "x86-interrupt" fn()` (active, abi_x86_interrupt, "1.17.0", Some(40180)), + + // Used to preserve symbols + (active, used, "1.17.0", None), ); declare_features! ( @@ -743,6 +746,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, From 9918471ee8d857d7aafd30947968e18563659ad1 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Sun, 5 Mar 2017 23:03:42 -0500 Subject: [PATCH 2/3] add tracking issue and feature-gate and run-make tests --- src/libsyntax/feature_gate.rs | 2 +- src/test/compile-fail/feature-gate-used.rs | 15 +++++++++++++++ src/test/run-make/used/Makefile | 12 ++++++++++++ src/test/run-make/used/used.rs | 17 +++++++++++++++++ 4 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 src/test/compile-fail/feature-gate-used.rs create mode 100644 src/test/run-make/used/Makefile create mode 100644 src/test/run-make/used/used.rs diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index cafb622ea469..cd25303c0c96 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -342,7 +342,7 @@ declare_features! ( (active, abi_x86_interrupt, "1.17.0", Some(40180)), // Used to preserve symbols - (active, used, "1.17.0", None), + (active, used, "1.17.0", Some(40289)), ); declare_features! ( diff --git a/src/test/compile-fail/feature-gate-used.rs b/src/test/compile-fail/feature-gate-used.rs new file mode 100644 index 000000000000..68679d7dac89 --- /dev/null +++ b/src/test/compile-fail/feature-gate-used.rs @@ -0,0 +1,15 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[used] +fn foo() {} +//~^^ ERROR the `#[used]` attribute is an experimental feature + +fn main() {} diff --git a/src/test/run-make/used/Makefile b/src/test/run-make/used/Makefile new file mode 100644 index 000000000000..70ac2e3802b8 --- /dev/null +++ b/src/test/run-make/used/Makefile @@ -0,0 +1,12 @@ +-include ../tools.mk + +ifdef IS_WINDOWS +# Do nothing on MSVC. +all: + exit 0 +else +all: + $(RUSTC) -C opt-level=3 --emit=obj used.rs + nm -C used.o | grep FOO + nm -C used.o | grep -v BAR +endif diff --git a/src/test/run-make/used/used.rs b/src/test/run-make/used/used.rs new file mode 100644 index 000000000000..186cd0fdf5e3 --- /dev/null +++ b/src/test/run-make/used/used.rs @@ -0,0 +1,17 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![crate_type = "lib"] +#![feature(used)] + +#[used] +static FOO: u32 = 0; + +static BAR: u32 = 0; From 0cb9b23389246c319d57d355d84596608cb41ea0 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Mon, 6 Mar 2017 11:18:56 -0500 Subject: [PATCH 3/3] fix location of the emitted object file --- src/test/run-make/used/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/run-make/used/Makefile b/src/test/run-make/used/Makefile index 70ac2e3802b8..650464e4d845 100644 --- a/src/test/run-make/used/Makefile +++ b/src/test/run-make/used/Makefile @@ -7,6 +7,6 @@ all: else all: $(RUSTC) -C opt-level=3 --emit=obj used.rs - nm -C used.o | grep FOO - nm -C used.o | grep -v BAR + nm -C $(TMPDIR)/used.o | grep FOO + nm -C $(TMPDIR)/used.o | grep -v BAR endif