Skip to content

Commit

Permalink
Rollup merge of rust-lang#39987 - japaric:used, r=arielb1
Browse files Browse the repository at this point in the history
[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
  • Loading branch information
frewsxcv authored Mar 9, 2017
2 parents 4618253 + 0cb9b23 commit e087642
Show file tree
Hide file tree
Showing 7 changed files with 81 additions and 1 deletion.
20 changes: 19 additions & 1 deletion src/librustc_trans/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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);
Expand Down
4 changes: 4 additions & 0 deletions src/librustc_trans/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
7 changes: 7 additions & 0 deletions src/librustc_trans/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,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>>,
Expand Down Expand Up @@ -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()),
Expand Down Expand Up @@ -786,6 +789,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
}
Expand Down
7 changes: 7 additions & 0 deletions src/libsyntax/feature_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,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", Some(40289)),
);

declare_features! (
Expand Down Expand Up @@ -742,6 +745,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,
Expand Down
15 changes: 15 additions & 0 deletions src/test/compile-fail/feature-gate-used.rs
Original file line number Diff line number Diff line change
@@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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() {}
12 changes: 12 additions & 0 deletions src/test/run-make/used/Makefile
Original file line number Diff line number Diff line change
@@ -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 $(TMPDIR)/used.o | grep FOO
nm -C $(TMPDIR)/used.o | grep -v BAR
endif
17 changes: 17 additions & 0 deletions src/test/run-make/used/used.rs
Original file line number Diff line number Diff line change
@@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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;

0 comments on commit e087642

Please sign in to comment.