Skip to content

Commit eaaf753

Browse files
authored
Rollup merge of rust-lang#39987 - japaric:used, r=arielb1
[RFC] #[used] attribute (For an explanation of what this feature does, read the commit message) I'd like to propose landing this as an experimental feature (experimental as in: no clear stabilization path -- like `asm!`, `#[linkage]`) as it's low maintenance (I think) and relevant to the "Usage in resource-constrained environments" exploration area. The main use case I see is running code before `main`. This could be used, for instance, to cheaply initialize an allocator before `main` where the alternative is to use `lazy_static` to initialize the allocator on its first use which it's more expensive (atomics) and doesn't work on ARM Cortex-M0 microcontrollers (no `AtomicUsize` on that platform) Here's a `std` example of that: ``` rust unsafe extern "C" fn before_main_1() { println!("Hello"); } unsafe extern "C" fn before_main_2() { println!("World"); } static INIT_ARRAY: [unsafe extern "C" fn(); 2] = [before_main_1, before_main_2]; fn main() { println!("Goodbye"); } ``` ``` $ rustc -C lto -C opt-level=3 before_main.rs $ ./before_main Hello World Goodbye ``` In general, this pattern could be used to let *dependencies* run code before `main` (which sounds like it could go very wrong in some cases). There are probably other use cases; I hope that the people I have cc-ed can comment on those. Note that I'm personally unsure if the above pattern is something we want to promote / allow and that's why I'm proposing this feature as experimental. If this leads to more footguns than benefits then we can just axe the feature. cc @nikomatsakis ^ I know you have some thoughts on having a process for experimental features though I'm fine with writing an RFC before landing this. - `dead_code` lint will have to be updated to special case `#[used]` symbols. - Should we extend `#[used]` to work on non-generic functions? cc rust-lang/rfcs#1002 cc rust-lang/rfcs#1459 cc @dpc @JinShil
2 parents 6cd15a0 + bbe5411 commit eaaf753

File tree

7 files changed

+84
-1
lines changed

7 files changed

+84
-1
lines changed

src/librustc_trans/base.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ use builder::Builder;
5050
use callee;
5151
use common::{C_bool, C_bytes_in_context, C_i32, C_uint};
5252
use collector::{self, TransItemCollectionMode};
53-
use common::{C_struct_in_context, C_u64, C_undef};
53+
use common::{C_struct_in_context, C_u64, C_undef, C_array};
5454
use common::CrateContext;
5555
use common::{type_is_zero_size, val_ty};
5656
use common;
@@ -1187,6 +1187,23 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
11871187
}
11881188
}
11891189

1190+
// Create the llvm.used variable
1191+
// This variable has type [N x i8*] and is stored in the llvm.metadata section
1192+
if !ccx.used_statics().borrow().is_empty() {
1193+
let name = CString::new("llvm.used").unwrap();
1194+
let section = CString::new("llvm.metadata").unwrap();
1195+
let array = C_array(Type::i8(&ccx).ptr_to(), &*ccx.used_statics().borrow());
1196+
1197+
unsafe {
1198+
let g = llvm::LLVMAddGlobal(ccx.llmod(),
1199+
val_ty(array).to_ref(),
1200+
name.as_ptr());
1201+
llvm::LLVMSetInitializer(g, array);
1202+
llvm::LLVMRustSetLinkage(g, llvm::Linkage::AppendingLinkage);
1203+
llvm::LLVMSetSection(g, section.as_ptr());
1204+
}
1205+
}
1206+
11901207
// Finalize debuginfo
11911208
if ccx.sess().opts.debuginfo != NoDebugInfo {
11921209
debuginfo::finalize(&ccx);

src/librustc_trans/consts.rs

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

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

279+
if attr::contains_name(attrs, "used") {
280+
// This static will be stored in the llvm.used variable which is an array of i8*
281+
let cast = llvm::LLVMConstPointerCast(g, Type::i8p(ccx).to_ref());
282+
ccx.used_statics().borrow_mut().push(cast);
283+
}
284+
279285
Ok(g)
280286
}
281287
}

src/librustc_trans/context.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,10 @@ pub struct LocalCrateContext<'tcx> {
132132
/// to constants.)
133133
statics_to_rauw: RefCell<Vec<(ValueRef, ValueRef)>>,
134134

135+
/// Statics that will be placed in the llvm.used variable
136+
/// See http://llvm.org/docs/LangRef.html#the-llvm-used-global-variable for details
137+
used_statics: RefCell<Vec<ValueRef>>,
138+
135139
lltypes: RefCell<FxHashMap<Ty<'tcx>, Type>>,
136140
llsizingtypes: RefCell<FxHashMap<Ty<'tcx>, Type>>,
137141
type_hashcodes: RefCell<FxHashMap<Ty<'tcx>, String>>,
@@ -587,6 +591,7 @@ impl<'tcx> LocalCrateContext<'tcx> {
587591
impl_method_cache: RefCell::new(FxHashMap()),
588592
closure_bare_wrapper_cache: RefCell::new(FxHashMap()),
589593
statics_to_rauw: RefCell::new(Vec::new()),
594+
used_statics: RefCell::new(Vec::new()),
590595
lltypes: RefCell::new(FxHashMap()),
591596
llsizingtypes: RefCell::new(FxHashMap()),
592597
type_hashcodes: RefCell::new(FxHashMap()),
@@ -754,6 +759,10 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> {
754759
&self.local().statics_to_rauw
755760
}
756761

762+
pub fn used_statics<'a>(&'a self) -> &'a RefCell<Vec<ValueRef>> {
763+
&self.local().used_statics
764+
}
765+
757766
pub fn lltypes<'a>(&'a self) -> &'a RefCell<FxHashMap<Ty<'tcx>, Type>> {
758767
&self.local().lltypes
759768
}

src/libsyntax/feature_gate.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,11 +334,15 @@ declare_features! (
334334
// `extern "x86-interrupt" fn()`
335335
(active, abi_x86_interrupt, "1.17.0", Some(40180)),
336336

337+
337338
// Allows the `catch {...}` expression
338339
(active, catch_expr, "1.17.0", Some(31436)),
339340

340341
// See rust-lang/rfcs#1414. Allows code like `let x: &'static u32 = &42` to work.
341342
(active, rvalue_static_promotion, "1.15.1", Some(38865)),
343+
344+
// Used to preserve symbols (see llvm.used)
345+
(active, used, "1.18.0", Some(40289)),
342346
);
343347

344348
declare_features! (
@@ -746,6 +750,10 @@ pub const BUILTIN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeG
746750
"unwind_attributes",
747751
"#[unwind] is experimental",
748752
cfg_fn!(unwind_attributes))),
753+
("used", Whitelisted, Gated(
754+
Stability::Unstable, "used",
755+
"the `#[used]` attribute is an experimental feature",
756+
cfg_fn!(used))),
749757

750758
// used in resolve
751759
("prelude_import", Whitelisted, Gated(Stability::Unstable,
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#[used]
12+
fn foo() {}
13+
//~^^ ERROR the `#[used]` attribute is an experimental feature
14+
15+
fn main() {}

src/test/run-make/used/Makefile

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
-include ../tools.mk
2+
3+
ifdef IS_WINDOWS
4+
# Do nothing on MSVC.
5+
all:
6+
exit 0
7+
else
8+
all:
9+
$(RUSTC) -C opt-level=3 --emit=obj used.rs
10+
nm -C $(TMPDIR)/used.o | grep FOO
11+
endif

src/test/run-make/used/used.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![crate_type = "lib"]
12+
#![feature(used)]
13+
14+
#[used]
15+
static FOO: u32 = 0;
16+
17+
static BAR: u32 = 0;

0 commit comments

Comments
 (0)