Skip to content

Commit 3055f41

Browse files
author
Jorge Aparicio
committed
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.
1 parent ba07bd5 commit 3055f41

File tree

4 files changed

+37
-1
lines changed

4 files changed

+37
-1
lines changed

src/librustc_trans/base.rs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ use builder::Builder;
5555
use callee::{Callee};
5656
use common::{C_bool, C_bytes_in_context, C_i32, C_uint};
5757
use collector::{self, TransItemCollectionMode};
58-
use common::{C_struct_in_context, C_u64, C_undef};
58+
use common::{C_struct_in_context, C_u64, C_undef, C_array};
5959
use common::CrateContext;
6060
use common::{fulfill_obligation};
6161
use common::{type_is_zero_size, val_ty};
@@ -1243,6 +1243,24 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
12431243
}
12441244
}
12451245

1246+
// Create llvm.used variable
1247+
if !ccx.used_statics().borrow().is_empty() {
1248+
debug!("llvm.used");
1249+
1250+
let name = CString::new("llvm.used").unwrap();
1251+
let section = CString::new("llvm.metadata").unwrap();
1252+
let array = C_array(Type::i8(&ccx).ptr_to(), &*ccx.used_statics().borrow());
1253+
1254+
unsafe {
1255+
let g = llvm::LLVMAddGlobal(ccx.llmod(),
1256+
val_ty(array).to_ref(),
1257+
name.as_ptr());
1258+
llvm::LLVMSetInitializer(g, array);
1259+
llvm::LLVMRustSetLinkage(g, llvm::Linkage::AppendingLinkage);
1260+
llvm::LLVMSetSection(g, section.as_ptr());
1261+
}
1262+
}
1263+
12461264
// Finalize debuginfo
12471265
if ccx.sess().opts.debuginfo != NoDebugInfo {
12481266
debuginfo::finalize(&ccx);

src/librustc_trans/consts.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,10 @@ pub fn trans_static<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
275275

276276
base::set_link_section(ccx, g, attrs);
277277

278+
if attr::contains_name(attrs, "used") {
279+
ccx.used_statics().borrow_mut().push(g);
280+
}
281+
278282
Ok(g)
279283
}
280284
}

src/librustc_trans/context.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,8 @@ pub struct LocalCrateContext<'tcx> {
136136
/// to constants.)
137137
statics_to_rauw: RefCell<Vec<(ValueRef, ValueRef)>>,
138138

139+
used_statics: RefCell<Vec<ValueRef>>,
140+
139141
lltypes: RefCell<FxHashMap<Ty<'tcx>, Type>>,
140142
llsizingtypes: RefCell<FxHashMap<Ty<'tcx>, Type>>,
141143
type_hashcodes: RefCell<FxHashMap<Ty<'tcx>, String>>,
@@ -606,6 +608,7 @@ impl<'tcx> LocalCrateContext<'tcx> {
606608
impl_method_cache: RefCell::new(FxHashMap()),
607609
closure_bare_wrapper_cache: RefCell::new(FxHashMap()),
608610
statics_to_rauw: RefCell::new(Vec::new()),
611+
used_statics: RefCell::new(Vec::new()),
609612
lltypes: RefCell::new(FxHashMap()),
610613
llsizingtypes: RefCell::new(FxHashMap()),
611614
type_hashcodes: RefCell::new(FxHashMap()),
@@ -786,6 +789,10 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> {
786789
&self.local().statics_to_rauw
787790
}
788791

792+
pub fn used_statics<'a>(&'a self) -> &'a RefCell<Vec<ValueRef>> {
793+
&self.local().used_statics
794+
}
795+
789796
pub fn lltypes<'a>(&'a self) -> &'a RefCell<FxHashMap<Ty<'tcx>, Type>> {
790797
&self.local().lltypes
791798
}

src/libsyntax/feature_gate.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,9 @@ declare_features! (
340340

341341
// `extern "x86-interrupt" fn()`
342342
(active, abi_x86_interrupt, "1.17.0", Some(40180)),
343+
344+
// Used to preserve symbols
345+
(active, used, "1.17.0", None),
343346
);
344347

345348
declare_features! (
@@ -743,6 +746,10 @@ pub const BUILTIN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeG
743746
"unwind_attributes",
744747
"#[unwind] is experimental",
745748
cfg_fn!(unwind_attributes))),
749+
("used", Whitelisted, Gated(
750+
Stability::Unstable, "used",
751+
"the `#[used]` attribute is an experimental feature",
752+
cfg_fn!(used))),
746753

747754
// used in resolve
748755
("prelude_import", Whitelisted, Gated(Stability::Unstable,

0 commit comments

Comments
 (0)